poafloc

Parser Of Arguments For Lines Of Commands
git clone git://git.dimitrijedobrota.com/poafloc.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |

commit31ee3b7c839842a6969085a8c52d9a83f225457b
parent08b98eba297b9146c8a8aecacb5a50833df2d4bf
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateFri, 14 Jun 2024 17:23:08 +0200

Rebrand to poafloc

Diffstat:
MCMakeLists.txt|+++++++++++++-------------
DargsConfig.cmake.in|------
Mdemo/CMakeLists.txt|++--
Mdemo/main.c|++++++++++----------
Mdemo/main.cpp|+++---
Dinclude/args.h|---------------------------------------------------------------------------------
Dinclude/args.hpp|---------------------------------------------------------------------------------
Ainclude/poafloc.h|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/poafloc.hpp|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ApoaflocConfig.cmake.in|++++++
Msrc/CMakeLists.txt|+++++++-------
Dsrc/args.cpp|---------------------------------------------------------------------------------
Msrc/c_bindings.cpp|++++++++--------
Msrc/help.cpp|++--
Asrc/poafloc.cpp|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/trie.cpp|++--

16 files changed, 572 insertions(+), 573 deletions(-)


diff --git a/CMakeLists.txt b/CMakeLists.txt

@@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.25.2)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(
args
VERSION 0.0.9
DESCRIPTION "Command Line Argument Parser"
poafloc
VERSION 1.0.0
DESCRIPTION "Parser Of Arguments For Lines Of Commands"
LANGUAGES CXX C
)

@@ -49,9 +49,9 @@ add_subdirectory(demo)

install(
TARGETS
args
poafloc
EXPORT
argsTargets
poaflocTargets
ARCHIVE
DESTINATION ${INSTALL_LIBDIR}
COMPONENT lib

@@ -62,15 +62,15 @@ install(

DESTINATION ${INSTALL_LIBDIR}
COMPONENT lib
PUBLIC_HEADER
DESTINATION ${INSTALL_INCLUDEDIR}/args
DESTINATION ${INSTALL_INCLUDEDIR}/poafloc
COMPONENT dev
)
install(
EXPORT
argsTargets
poaflocTargets
NAMESPACE
"args::"
"poafloc::"
DESTINATION
${INSTALL_CMAKEDIR}
COMPONENT

@@ -80,21 +80,21 @@ install(

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/argsConfigVersion.cmake
${CMAKE_CURRENT_BINARY_DIR}/poaflocConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/argsConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/argsConfig.cmake
${PROJECT_SOURCE_DIR}/poaflocConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/poaflocConfig.cmake
INSTALL_DESTINATION ${INSTALL_CMAKEDIR}
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/argsConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/argsConfigVersion.cmake
${CMAKE_CURRENT_BINARY_DIR}/poaflocConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/poaflocConfigVersion.cmake
DESTINATION
${INSTALL_CMAKEDIR}
)

diff --git a/argsConfig.cmake.in b/argsConfig.cmake.in

@@ -1,6 +0,0 @@

@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/argsTargets.cmake")
check_required_components(
"args"
)

diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt

@@ -1,5 +1,5 @@

add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE args)
target_link_libraries(demo PRIVATE poafloc)
target_include_directories(demo PRIVATE ../include)
set_target_properties(demo PROPERTIES LANGUAGE CXX)
set_target_properties(demo PROPERTIES

@@ -8,7 +8,7 @@ set_target_properties(demo PROPERTIES

)
add_executable(cdemo main.c)
target_link_libraries(cdemo PRIVATE args)
target_link_libraries(cdemo PRIVATE poafloc)
target_include_directories(cdemo PRIVATE ../include)
set_target_properties(cdemo PROPERTIES LANGUAGE C)
set_target_properties(cdemo PROPERTIES

diff --git a/demo/main.c b/demo/main.c

@@ -1,4 +1,4 @@

#include "args.h"
#include "poafloc.h"
#include <stdio.h>

@@ -13,8 +13,8 @@ typedef struct {

int relocatable;
} arguments_t;
int parse_opt(int key, const char *arg, args_parser_t *parser) {
arguments_t *arguments = (arguments_t *)args_parser_input(parser);
int parse_opt(int key, const char *arg, poafloc_parser_t *parser) {
arguments_t *arguments = (arguments_t *)poafloc_parser_input(parser);
switch (key) {
case 777: arguments->debug = 1; break;

@@ -29,8 +29,8 @@ int parse_opt(int key, const char *arg, args_parser_t *parser) {

case 'o': arguments->output_file = arg ? arg : "stdout"; break;
case 'i': arguments->input_file = arg; break;
// case Parser::Key::ARG: arguments->args.push_back(arg); break;
case ARGS_KEY_ERROR: fprintf(stderr, "handled error\n");
case ARGS_KEY_INIT:
case POAFLOC_KEY_ERROR: fprintf(stderr, "handled error\n");
case POAFLOC_KEY_INIT:
arguments->input_file = "stdin";
arguments->output_file = "stdout";
}

@@ -39,22 +39,22 @@ int parse_opt(int key, const char *arg, args_parser_t *parser) {

}
// clang-format off
static const args_option_t options[] = {
static const poafloc_option_t options[] = {
{ 0, 'R', 0, 0, "random 0-group option"},
{ 0, 0, 0, 0, "Program mode", 1},
{"relocatable", 'r', 0, 0, "Output in relocatable format"},
{ "hex", 'h', 0, 0, "Output in hex format"},
{"hexadecimal", 0, 0, ARGS_OPTION_ALIAS | ARGS_OPTION_HIDDEN},
{"hexadecimal", 0, 0, POAFLOC_OPTION_ALIAS | POAFLOC_OPTION_HIDDEN},
{ 0, 0, 0, 0, "For developers", 4},
{ "debug", 777, 0, 0, "Enable debugging mode"},
{ 0, 0, 0, 0, "Input/output", 3},
{ "output", 'o', "file", ARGS_OPTION_ARG_OPTIONAL, "Output file, default stdout"},
{ "output", 'o', "file", POAFLOC_OPTION_ARG_OPTIONAL, "Output file, default stdout"},
{ 0, 'i', "file", 0, "Input file"},
{ 0, 0, 0, 0, "Informational Options", -1},
{0},
};
static const args_argp_t argp = {
static const poafloc_arg_t argp = {
options, parse_opt, "doc string\nother usage",
"First half of the message\vsecond half of the message"
};

@@ -63,7 +63,7 @@ static const args_argp_t argp = {

int main(int argc, char *argv[]) {
arguments_t arguments = {0};
if (args_parse(&argp, argc, argv, 0, &arguments)) {
if (poafloc_parse(&argp, argc, argv, 0, &arguments)) {
error("There was an error while parsing arguments");
return 1;
}

diff --git a/demo/main.cpp b/demo/main.cpp

@@ -1,10 +1,10 @@

#include "args.hpp"
#include "poafloc.hpp"
#include <cstdint>
#include <iostream>
#include <vector>
using namespace args;
using namespace poafloc;
void error(const std::string &message) { std::cerr << message << std::endl; }
struct arguments_t {

@@ -56,7 +56,7 @@ static const option_t options[] = {

{0},
};
static const argp_t argp = {
static const arg_t argp = {
options, parse_opt, "doc string\nother usage",
"First half of the message\vsecond half of the message"
};

diff --git a/include/args.h b/include/args.h

@@ -1,122 +0,0 @@

#ifndef ARGS_H
#define ARGS_H
#ifdef __cplusplus
#include <cstdio>
#define MANGLE_ENUM(enumn, name) name
#define ENUM_OPTION Option
#define ENUM_KEY Key
#define ENUM_HELP Help
#define ENUM_PARSE Parse
extern "C" {
namespace args {
struct Parser;
typedef Parser args_parser_t;
#else
#include <stdio.h>
#define MANGLE_ENUM(enumn, name) ARGS_##enumn##_##name
#define ENUM_OPTION args_option_e
#define ENUM_KEY args_key_e
#define ENUM_HELP args_help_e
#define ENUM_PARSE args_parse_e
struct __Parser;
typedef struct __Parser args_parser_t;
#endif
typedef struct {
char const *name;
int key;
char const *arg;
int flags;
char const *message;
int group;
} args_option_t;
typedef int (*args_parse_f)(int key, const char *arg, args_parser_t *parser);
typedef struct {
args_option_t const *options;
args_parse_f parse;
char const *doc;
char const *message;
} args_argp_t;
enum ENUM_OPTION {
MANGLE_ENUM(OPTION, ARG_OPTIONAL) = 0x1,
MANGLE_ENUM(OPTION, HIDDEN) = 0x2,
MANGLE_ENUM(OPTION, ALIAS) = 0x4,
};
enum ENUM_KEY {
MANGLE_ENUM(KEY, ARG) = 0,
MANGLE_ENUM(KEY, END) = 0x1000001,
MANGLE_ENUM(KEY, NO_ARGS) = 0x1000002,
MANGLE_ENUM(KEY, INIT) = 0x1000003,
MANGLE_ENUM(KEY, SUCCESS) = 0x1000004,
MANGLE_ENUM(KEY, ERROR) = 0x1000005,
};
enum ENUM_HELP {
MANGLE_ENUM(HELP, SHORT_USAGE) = 0x1,
MANGLE_ENUM(HELP, USAGE) = 0x2,
MANGLE_ENUM(HELP, SEE) = 0x4,
MANGLE_ENUM(HELP, LONG) = 0x8,
MANGLE_ENUM(HELP, EXIT_ERR) = 0x10,
MANGLE_ENUM(HELP, EXIT_OK) = 0x20,
MANGLE_ENUM(HELP, STD_ERR) = MANGLE_ENUM(HELP, SEE) |
MANGLE_ENUM(HELP, EXIT_ERR),
MANGLE_ENUM(HELP, STD_HELP) = MANGLE_ENUM(HELP, LONG) |
MANGLE_ENUM(HELP, EXIT_OK),
MANGLE_ENUM(HELP, STD_USAGE) = MANGLE_ENUM(HELP, USAGE) |
MANGLE_ENUM(HELP, EXIT_ERR),
};
enum ENUM_PARSE {
MANGLE_ENUM(PARSE, NO_ERRS) = 0x1,
// MANGLE_ENUM(PARSE, NO_ARGS),
MANGLE_ENUM(PARSE, NO_HELP) = 0x2,
MANGLE_ENUM(PARSE, NO_EXIT) = 0x4,
MANGLE_ENUM(PARSE, SILENT) = 0x8,
MANGLE_ENUM(PARSE, IN_ORDER) = 0x10,
};
#if !defined __cplusplus || defined WITH_C_BINDINGS
void *args_parser_input(args_parser_t *parser);
void args_usage(args_parser_t *parser);
void args_help(const args_parser_t *state, FILE *stream, unsigned flags);
int args_parse(const args_argp_t *argp, int argc, char *argv[], unsigned flags,
void *input);
void args_failure(const args_parser_t *parser, int status, int errnum,
const char *fmt, ...);
#endif
#undef MANGLE_ENUM
#undef ENUM_OPTION
#undef ENUM_KEY
#ifdef __cplusplus
} // namespace args
} // extern "C"
#endif
#endif

diff --git a/include/args.hpp b/include/args.hpp

@@ -1,111 +0,0 @@

#ifndef ARGS_HPP
#define ARGS_HPP
#include "args.h"
#include <cstdarg>
#include <string>
#include <unordered_map>
#include <vector>
namespace args {
using option_t = args_option_t;
using argp_t = args_argp_t;
int parse(const argp_t *argp, int argc, char *argv[], unsigned flags,
void *input) noexcept;
void usage(const Parser *parser);
void help(const Parser *parser, FILE *stream, unsigned flags);
void failure(const Parser *parser, int status, int errnum, const char *fmt,
va_list args);
void failure(const Parser *parser, int status, int errnum, const char *fmt,
...);
class Parser {
public:
void *input() const { return m_input; }
const char *name() const { return m_name; }
unsigned flags() const { return m_flags; }
private:
friend int parse(const argp_t *, int, char **, unsigned, void *) noexcept;
friend void help(const Parser *parser, FILE *stream, unsigned flags);
Parser(const argp_t *argp, unsigned flags, void *input);
Parser(const Parser &) = delete;
Parser(Parser &&) = delete;
Parser &operator=(const Parser &) = delete;
Parser &operator=(Parser &&) = delete;
~Parser() noexcept = default;
int parse(int argc, char *argv[]);
int handle_unknown(bool shrt, const char *argv);
int handle_missing(bool shrt, const char *argv);
int handle_excess(const char *argv);
void print_other_usages(FILE *stream) const;
void help(FILE *stream) const;
void usage(FILE *stream) const;
void see(FILE *stream) const;
static const char *basename(const char *name);
struct help_entry_t {
help_entry_t(const char *arg, const char *message, int group,
bool opt = false)
: arg(arg), message(message), group(group), opt(opt) {}
void push(char sh) { opt_short.push_back(sh); }
void push(const char *lg) { opt_long.push_back(lg); }
bool operator<(const help_entry_t &rhs) const;
const char *arg;
const char *message;
int group;
bool opt;
std::vector<const char *> opt_long;
std::vector<char> opt_short;
};
class trie_t {
public:
trie_t() = default;
trie_t(const trie_t &) = delete;
trie_t(trie_t &&) = delete;
trie_t &operator=(const trie_t &) = delete;
trie_t &operator=(trie_t &&) = delete;
~trie_t() noexcept;
bool insert(const char *option, int key);
int get(const char *option) const;
private:
static bool is_valid(const char *option);
trie_t *children[26] = {0};
int count = 0;
int key = 0;
bool terminal = false;
};
const argp_t *argp;
unsigned m_flags;
void *m_input;
const char *m_name;
std::unordered_map<int, const option_t *> options;
std::vector<help_entry_t> help_entries;
trie_t trie;
};
} // namespace args
#endif

diff --git a/include/poafloc.h b/include/poafloc.h

@@ -0,0 +1,121 @@

#ifndef POAFLOC_POAFLOC_H
#define POAFLOC_POAFLOC_H
#ifdef __cplusplus
#include <cstdio>
#define MANGLE_ENUM(enumn, name) name
#define ENUM_OPTION Option
#define ENUM_KEY Key
#define ENUM_HELP Help
#define ENUM_PARSE Parse
extern "C" {
namespace poafloc {
struct Parser;
typedef Parser poafloc_parser_t;
#else
#include <stdio.h>
#define MANGLE_ENUM(enumn, name) POAFLOC_##enumn##_##name
#define ENUM_OPTION poafloc_option_e
#define ENUM_KEY poafloc_key_e
#define ENUM_HELP poafloc_help_e
#define ENUM_PARSE poafloc_parse_e
struct __Parser;
typedef struct __Parser poafloc_parser_t;
#endif
typedef struct {
char const *name;
int key;
char const *arg;
int flags;
char const *message;
int group;
} poafloc_option_t;
typedef int (*poafloc_parse_f)(int key, const char *arg, poafloc_parser_t *parser);
typedef struct {
poafloc_option_t const *options;
poafloc_parse_f parse;
char const *doc;
char const *message;
} poafloc_arg_t;
enum ENUM_OPTION {
MANGLE_ENUM(OPTION, ARG_OPTIONAL) = 0x1,
MANGLE_ENUM(OPTION, HIDDEN) = 0x2,
MANGLE_ENUM(OPTION, ALIAS) = 0x4,
};
enum ENUM_KEY {
MANGLE_ENUM(KEY, ARG) = 0,
MANGLE_ENUM(KEY, END) = 0x1000001,
MANGLE_ENUM(KEY, NO_ARGS) = 0x1000002,
MANGLE_ENUM(KEY, INIT) = 0x1000003,
MANGLE_ENUM(KEY, SUCCESS) = 0x1000004,
MANGLE_ENUM(KEY, ERROR) = 0x1000005,
};
enum ENUM_HELP {
MANGLE_ENUM(HELP, SHORT_USAGE) = 0x1,
MANGLE_ENUM(HELP, USAGE) = 0x2,
MANGLE_ENUM(HELP, SEE) = 0x4,
MANGLE_ENUM(HELP, LONG) = 0x8,
MANGLE_ENUM(HELP, EXIT_ERR) = 0x10,
MANGLE_ENUM(HELP, EXIT_OK) = 0x20,
MANGLE_ENUM(HELP, STD_ERR) = MANGLE_ENUM(HELP, SEE) |
MANGLE_ENUM(HELP, EXIT_ERR),
MANGLE_ENUM(HELP, STD_HELP) = MANGLE_ENUM(HELP, LONG) |
MANGLE_ENUM(HELP, EXIT_OK),
MANGLE_ENUM(HELP, STD_USAGE) = MANGLE_ENUM(HELP, USAGE) |
MANGLE_ENUM(HELP, EXIT_ERR),
};
enum ENUM_PARSE {
MANGLE_ENUM(PARSE, NO_ERRS) = 0x1,
MANGLE_ENUM(PARSE, NO_HELP) = 0x2,
MANGLE_ENUM(PARSE, NO_EXIT) = 0x4,
MANGLE_ENUM(PARSE, SILENT) = 0x8,
MANGLE_ENUM(PARSE, IN_ORDER) = 0x10,
};
#if !defined __cplusplus || defined WITH_C_BINDINGS
void *poafloc_parser_input(poafloc_parser_t *parser);
void poafloc_usage(poafloc_parser_t *parser);
void poafloc_help(const poafloc_parser_t *state, FILE *stream, unsigned flags);
int poafloc_parse(const poafloc_arg_t *argp, int argc, char *argv[], unsigned flags,
void *input);
void poafloc_failure(const poafloc_parser_t *parser, int status, int errnum,
const char *fmt, ...);
#endif
#undef MANGLE_ENUM
#undef ENUM_OPTION
#undef ENUM_KEY
#ifdef __cplusplus
} // namespace poafloc
} // extern "C"
#endif
#endif

diff --git a/include/poafloc.hpp b/include/poafloc.hpp

@@ -0,0 +1,111 @@

#ifndef POAFLOC_POAFLOC_HPP
#define POAFLOC_POAFLOC_HPP
#include "poafloc.h"
#include <cstdarg>
#include <string>
#include <unordered_map>
#include <vector>
namespace poafloc {
using option_t = poafloc_option_t;
using arg_t = poafloc_arg_t;
int parse(const arg_t *argp, int argc, char *argv[], unsigned flags,
void *input) noexcept;
void usage(const Parser *parser);
void help(const Parser *parser, FILE *stream, unsigned flags);
void failure(const Parser *parser, int status, int errnum, const char *fmt,
va_list args);
void failure(const Parser *parser, int status, int errnum, const char *fmt,
...);
class Parser {
public:
void *input() const { return m_input; }
const char *name() const { return m_name; }
unsigned flags() const { return m_flags; }
private:
friend int parse(const arg_t *, int, char **, unsigned, void *) noexcept;
friend void help(const Parser *parser, FILE *stream, unsigned flags);
Parser(const arg_t *argp, unsigned flags, void *input);
Parser(const Parser &) = delete;
Parser(Parser &&) = delete;
Parser &operator=(const Parser &) = delete;
Parser &operator=(Parser &&) = delete;
~Parser() noexcept = default;
int parse(int argc, char *argv[]);
int handle_unknown(bool shrt, const char *argv);
int handle_missing(bool shrt, const char *argv);
int handle_excess(const char *argv);
void print_other_usages(FILE *stream) const;
void help(FILE *stream) const;
void usage(FILE *stream) const;
void see(FILE *stream) const;
static const char *basename(const char *name);
struct help_entry_t {
help_entry_t(const char *arg, const char *message, int group,
bool opt = false)
: arg(arg), message(message), group(group), opt(opt) {}
void push(char sh) { opt_short.push_back(sh); }
void push(const char *lg) { opt_long.push_back(lg); }
bool operator<(const help_entry_t &rhs) const;
const char *arg;
const char *message;
int group;
bool opt;
std::vector<const char *> opt_long;
std::vector<char> opt_short;
};
class trie_t {
public:
trie_t() = default;
trie_t(const trie_t &) = delete;
trie_t(trie_t &&) = delete;
trie_t &operator=(const trie_t &) = delete;
trie_t &operator=(trie_t &&) = delete;
~trie_t() noexcept;
bool insert(const char *option, int key);
int get(const char *option) const;
private:
static bool is_valid(const char *option);
trie_t *children[26] = {0};
int count = 0;
int key = 0;
bool terminal = false;
};
const arg_t *argp;
unsigned m_flags;
void *m_input;
const char *m_name;
std::unordered_map<int, const option_t *> options;
std::vector<help_entry_t> help_entries;
trie_t trie;
};
} // namespace poafloc
#endif

diff --git a/poaflocConfig.cmake.in b/poaflocConfig.cmake.in

@@ -0,0 +1,6 @@

@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/poaflocTargets.cmake")
check_required_components(
"poafloc"
)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt

@@ -1,14 +1,14 @@

add_library(args STATIC args.cpp help.cpp trie.cpp c_bindings.cpp)
target_include_directories(args PRIVATE ../include)
target_compile_definitions(args PRIVATE WITH_C_BINDINGS)
set_target_properties(args PROPERTIES LINKER_LANGUAGE CXX)
add_library(poafloc STATIC poafloc.cpp help.cpp trie.cpp c_bindings.cpp)
target_include_directories(poafloc PRIVATE ../include)
target_compile_definitions(poafloc PRIVATE WITH_C_BINDINGS)
set_target_properties(poafloc PROPERTIES LINKER_LANGUAGE CXX)
set_target_properties(args PROPERTIES
set_target_properties(poafloc PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
OUTPUT_NAME "args"
OUTPUT_NAME "poafloc"
DEBUG_POSTFIX "d"
PUBLIC_HEADER "include/args.hpp;include/args.h"
PUBLIC_HEADER "include/poafloc.hpp;include/poafloc.h"
MACOSX_RPATH ON
WINDOWS_EXPORT_ALL_SYMBOLS ON
)

diff --git a/src/args.cpp b/src/args.cpp

@@ -1,287 +0,0 @@

#include "args.hpp"
#include <algorithm>
#include <cstring>
#include <format>
#include <iostream>
#include <sstream>
namespace args {
int parse(const argp_t *argp, int argc, char *argv[], unsigned flags,
void *input) noexcept {
Parser parser(argp, flags, input);
return parser.parse(argc, argv);
}
void usage(const Parser *parser) { help(parser, stderr, Help::STD_USAGE); }
void help(const Parser *parser, FILE *stream, unsigned flags) {
if (!parser || !stream) return;
if (flags & LONG) parser->help(stream);
else if (flags & USAGE) parser->usage(stream);
else if (flags & SEE) parser->see(stream);
if (parser->flags() & NO_EXIT) return;
if (flags & EXIT_ERR) exit(2);
if (flags & EXIT_OK) exit(0);
}
void failure(const Parser *parser, int status, int errnum, const char *fmt,
std::va_list args) {
(void)errnum;
std::fprintf(stderr, "%s: ", parser->name());
std::vfprintf(stderr, fmt, args);
std::putc('\n', stderr);
if (status) exit(status);
}
void failure(const Parser *parser, int status, int errnum, const char *fmt,
...) {
std::va_list args;
va_start(args, fmt);
failure(parser, status, errnum, fmt, args);
va_end(args);
}
Parser::Parser(const argp_t *argp, unsigned flags, void *input)
: argp(argp), m_flags(flags), m_input(input) {
int group = 0, key_last = 0;
bool hidden = false;
for (int i = 0; true; i++) {
const auto &opt = argp->options[i];
if (!opt.name && !opt.key && !opt.message) break;
if (!opt.name && !opt.key) {
group = opt.group ? opt.group : group + 1;
help_entries.emplace_back(nullptr, opt.message, group);
continue;
}
if (!opt.key) {
// non alias without a key, silently ignoring
if (!(opt.flags & ALIAS)) continue;
// nothing to alias, silently ignoring
if (!key_last) continue;
// option not valid, silently ignoring
if (!trie.insert(opt.name, key_last)) continue;
if (hidden) continue;
if (opt.flags & Option::HIDDEN) continue;
help_entries.back().push(opt.name);
} else {
// duplicate key, silently ignoring
if (options.count(opt.key)) continue;
if (opt.name) trie.insert(opt.name, opt.key);
options[key_last = opt.key] = &opt;
bool arg_opt = opt.flags & Option::ARG_OPTIONAL;
if (!(opt.flags & ALIAS)) {
if ((hidden = opt.flags & Option::HIDDEN)) continue;
help_entries.emplace_back(opt.arg, opt.message, group,
arg_opt);
if (opt.name) help_entries.back().push(opt.name);
if (std::isprint(opt.key)) help_entries.back().push(opt.key);
} else {
// nothing to alias, silently ignoring
if (!key_last) continue;
if (hidden) continue;
if (opt.flags & Option::HIDDEN) continue;
if (opt.name) help_entries.back().push(opt.name);
if (std::isprint(opt.key)) help_entries.back().push(opt.key);
}
}
}
if (!(m_flags & NO_HELP)) {
help_entries.emplace_back(nullptr, "Give this help list", -1);
help_entries.back().push("help");
help_entries.back().push('?');
help_entries.emplace_back(nullptr, "Give a short usage message", -1);
help_entries.back().push("usage");
}
std::sort(begin(help_entries), end(help_entries));
}
int Parser::parse(int argc, char *argv[]) {
std::vector<const char *> args;
int arg_cnt = 0, err_code = 0, i;
const bool is_help = !(m_flags & NO_HELP);
const bool is_error = !(m_flags & NO_ERRS);
m_name = basename(argv[0]);
argp->parse(Key::INIT, 0, this);
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
if (m_flags & IN_ORDER) argp->parse(Key::ARG, argv[i], this);
else args.push_back(argv[i]);
arg_cnt++;
continue;
}
// stop parsing options, rest are normal arguments
if (!std::strcmp(argv[i], "--")) break;
if (argv[i][1] != '-') { // short option
const char *opt = argv[i] + 1;
// loop over ganged options
for (int j = 0; opt[j]; j++) {
const char key = opt[j];
if (is_help && key == '?') {
if (is_error) ::args::help(this, stderr, STD_HELP);
continue;
}
if (!options.count(key)) {
err_code = handle_unknown(1, argv[i]);
goto error;
}
const auto *option = options[key];
bool is_opt = option->flags & ARG_OPTIONAL;
if (!option->arg) argp->parse(key, nullptr, this);
if (opt[j + 1] != 0) {
argp->parse(key, opt + j + 1, this);
break;
} else if (is_opt) argp->parse(key, nullptr, this);
else if (i + 1 != argc) {
argp->parse(key, argv[++i], this);
break;
} else {
err_code = handle_missing(1, argv[i]);
goto error;
}
}
} else { // long option
const char *opt = argv[i] + 2;
const auto is_eq = std::strchr(opt, '=');
std::string opt_s = !is_eq ? opt : std::string(opt, is_eq - opt);
if (is_help && opt_s == "help") {
if (is_eq) {
err_code = handle_excess(argv[i]);
goto error;
}
if (is_error) ::args::help(this, stderr, STD_HELP);
continue;
}
if (is_help && opt_s == "usage") {
if (is_eq) {
err_code = handle_excess(argv[i]);
goto error;
}
if (is_error) ::args::help(this, stderr, STD_USAGE);
continue;
}
const int key = trie.get(opt_s.data());
if (!key) {
err_code = handle_unknown(0, argv[i]);
goto error;
}
const auto *option = options[key];
if (!option->arg && is_eq) {
err_code = handle_excess(argv[i]);
goto error;
}
bool is_opt = option->flags & ARG_OPTIONAL;
if (!option->arg) argp->parse(key, nullptr, this);
else if (is_eq) argp->parse(key, is_eq + 1, this);
else if (is_opt) argp->parse(key, nullptr, this);
else if (i + 1 != argc) argp->parse(key, argv[++i], this);
else {
err_code = handle_missing(0, argv[i]);
goto error;
}
}
}
// parse previous arguments if IN_ORDER is not set
for (const auto arg : args) {
argp->parse(Key::ARG, arg, this);
}
// parse rest argv as normal arguments
for (i = i + 1; i < argc; i++) {
argp->parse(Key::ARG, argv[i], this);
arg_cnt++;
}
if (!arg_cnt) argp->parse(Key::NO_ARGS, 0, this);
argp->parse(Key::END, 0, this);
argp->parse(Key::SUCCESS, 0, this);
return 0;
error:
return err_code;
}
int Parser::handle_unknown(bool shrt, const char *argv) {
if (m_flags & NO_ERRS) return argp->parse(Key::ERROR, 0, this);
static const char *const unknown_fmt[2] = {
"unrecognized option '-%s'\n",
"invalid option -- '%s'\n",
};
failure(this, 1, 0, unknown_fmt[shrt], argv + 1);
see(stderr);
if (m_flags & NO_EXIT) return 1;
exit(1);
}
int Parser::handle_missing(bool shrt, const char *argv) {
if (m_flags & NO_ERRS) return argp->parse(Key::ERROR, 0, this);
static const char *const missing_fmt[2] = {
"option '-%s' requires an argument\n",
"option requires an argument -- '%s'\n",
};
failure(this, 2, 0, missing_fmt[shrt], argv + 1);
see(stderr);
if (m_flags & NO_EXIT) return 2;
exit(2);
}
int Parser::handle_excess(const char *argv) {
if (m_flags & NO_ERRS) return argp->parse(Key::ERROR, 0, this);
failure(this, 3, 0, "option '%s' doesn't allow an argument\n", argv);
see(stderr);
if (m_flags & NO_EXIT) return 3;
exit(3);
}
const char *Parser::basename(const char *name) {
const char *name_sh = std::strrchr(name, '/');
return name_sh ? name_sh + 1 : name;
}
} // namespace args

diff --git a/src/c_bindings.cpp b/src/c_bindings.cpp

@@ -1,24 +1,24 @@

#include "args.h"
#include "args.hpp"
#include "poafloc.h"
#include "poafloc.hpp"
#include <cstdarg>
namespace args {
namespace poafloc {
int args_parse(const args_argp_t *argp, int argc, char *argv[], unsigned flags,
int poafloc_parse(const poafloc_arg_t *argp, int argc, char *argv[], unsigned flags,
void *input) {
return parse(argp, argc, argv, flags, input);
}
void *args_parser_input(args_parser_t *parser) { return parser->input(); }
void *poafloc_parser_input(poafloc_parser_t *parser) { return parser->input(); }
void args_usage(args_parser_t *parser) { return usage(parser); }
void poafloc_usage(poafloc_parser_t *parser) { return usage(parser); }
void args_help(const args_parser_t *parser, FILE *stream, unsigned flags) {
void poafloc_help(const poafloc_parser_t *parser, FILE *stream, unsigned flags) {
help(parser, stream, flags);
}
void args_failure(const args_parser_t *parser, int status, int errnum,
void poafloc_failure(const poafloc_parser_t *parser, int status, int errnum,
const char *fmt, ...) {
std::va_list args;
va_start(args, fmt);

diff --git a/src/help.cpp b/src/help.cpp

@@ -1,10 +1,10 @@

#include "args.hpp"
#include "poafloc.hpp"
#include <cstring>
#include <format>
#include <sstream>
namespace args {
namespace poafloc {
bool Parser::help_entry_t::operator<(const help_entry_t &rhs) const {
if (group != rhs.group) {

diff --git a/src/poafloc.cpp b/src/poafloc.cpp

@@ -0,0 +1,287 @@

#include "poafloc.hpp"
#include <algorithm>
#include <cstring>
#include <format>
#include <iostream>
#include <sstream>
namespace poafloc {
int parse(const arg_t *argp, int argc, char *argv[], unsigned flags,
void *input) noexcept {
Parser parser(argp, flags, input);
return parser.parse(argc, argv);
}
void usage(const Parser *parser) { help(parser, stderr, Help::STD_USAGE); }
void help(const Parser *parser, FILE *stream, unsigned flags) {
if (!parser || !stream) return;
if (flags & LONG) parser->help(stream);
else if (flags & USAGE) parser->usage(stream);
else if (flags & SEE) parser->see(stream);
if (parser->flags() & NO_EXIT) return;
if (flags & EXIT_ERR) exit(2);
if (flags & EXIT_OK) exit(0);
}
void failure(const Parser *parser, int status, int errnum, const char *fmt,
std::va_list args) {
(void)errnum;
std::fprintf(stderr, "%s: ", parser->name());
std::vfprintf(stderr, fmt, args);
std::putc('\n', stderr);
if (status) exit(status);
}
void failure(const Parser *parser, int status, int errnum, const char *fmt,
...) {
std::va_list args;
va_start(args, fmt);
failure(parser, status, errnum, fmt, args);
va_end(args);
}
Parser::Parser(const arg_t *argp, unsigned flags, void *input)
: argp(argp), m_flags(flags), m_input(input) {
int group = 0, key_last = 0;
bool hidden = false;
for (int i = 0; true; i++) {
const auto &opt = argp->options[i];
if (!opt.name && !opt.key && !opt.message) break;
if (!opt.name && !opt.key) {
group = opt.group ? opt.group : group + 1;
help_entries.emplace_back(nullptr, opt.message, group);
continue;
}
if (!opt.key) {
// non alias without a key, silently ignoring
if (!(opt.flags & ALIAS)) continue;
// nothing to alias, silently ignoring
if (!key_last) continue;
// option not valid, silently ignoring
if (!trie.insert(opt.name, key_last)) continue;
if (hidden) continue;
if (opt.flags & Option::HIDDEN) continue;
help_entries.back().push(opt.name);
} else {
// duplicate key, silently ignoring
if (options.count(opt.key)) continue;
if (opt.name) trie.insert(opt.name, opt.key);
options[key_last = opt.key] = &opt;
bool arg_opt = opt.flags & Option::ARG_OPTIONAL;
if (!(opt.flags & ALIAS)) {
if ((hidden = opt.flags & Option::HIDDEN)) continue;
help_entries.emplace_back(opt.arg, opt.message, group,
arg_opt);
if (opt.name) help_entries.back().push(opt.name);
if (std::isprint(opt.key)) help_entries.back().push(opt.key);
} else {
// nothing to alias, silently ignoring
if (!key_last) continue;
if (hidden) continue;
if (opt.flags & Option::HIDDEN) continue;
if (opt.name) help_entries.back().push(opt.name);
if (std::isprint(opt.key)) help_entries.back().push(opt.key);
}
}
}
if (!(m_flags & NO_HELP)) {
help_entries.emplace_back(nullptr, "Give this help list", -1);
help_entries.back().push("help");
help_entries.back().push('?');
help_entries.emplace_back(nullptr, "Give a short usage message", -1);
help_entries.back().push("usage");
}
std::sort(begin(help_entries), end(help_entries));
}
int Parser::parse(int argc, char *argv[]) {
std::vector<const char *> args;
int arg_cnt = 0, err_code = 0, i;
const bool is_help = !(m_flags & NO_HELP);
const bool is_error = !(m_flags & NO_ERRS);
m_name = basename(argv[0]);
argp->parse(Key::INIT, 0, this);
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
if (m_flags & IN_ORDER) argp->parse(Key::ARG, argv[i], this);
else args.push_back(argv[i]);
arg_cnt++;
continue;
}
// stop parsing options, rest are normal arguments
if (!std::strcmp(argv[i], "--")) break;
if (argv[i][1] != '-') { // short option
const char *opt = argv[i] + 1;
// loop over ganged options
for (int j = 0; opt[j]; j++) {
const char key = opt[j];
if (is_help && key == '?') {
if (is_error) ::poafloc::help(this, stderr, STD_HELP);
continue;
}
if (!options.count(key)) {
err_code = handle_unknown(1, argv[i]);
goto error;
}
const auto *option = options[key];
bool is_opt = option->flags & ARG_OPTIONAL;
if (!option->arg) argp->parse(key, nullptr, this);
if (opt[j + 1] != 0) {
argp->parse(key, opt + j + 1, this);
break;
} else if (is_opt) argp->parse(key, nullptr, this);
else if (i + 1 != argc) {
argp->parse(key, argv[++i], this);
break;
} else {
err_code = handle_missing(1, argv[i]);
goto error;
}
}
} else { // long option
const char *opt = argv[i] + 2;
const auto is_eq = std::strchr(opt, '=');
std::string opt_s = !is_eq ? opt : std::string(opt, is_eq - opt);
if (is_help && opt_s == "help") {
if (is_eq) {
err_code = handle_excess(argv[i]);
goto error;
}
if (is_error) ::poafloc::help(this, stderr, STD_HELP);
continue;
}
if (is_help && opt_s == "usage") {
if (is_eq) {
err_code = handle_excess(argv[i]);
goto error;
}
if (is_error) ::poafloc::help(this, stderr, STD_USAGE);
continue;
}
const int key = trie.get(opt_s.data());
if (!key) {
err_code = handle_unknown(0, argv[i]);
goto error;
}
const auto *option = options[key];
if (!option->arg && is_eq) {
err_code = handle_excess(argv[i]);
goto error;
}
bool is_opt = option->flags & ARG_OPTIONAL;
if (!option->arg) argp->parse(key, nullptr, this);
else if (is_eq) argp->parse(key, is_eq + 1, this);
else if (is_opt) argp->parse(key, nullptr, this);
else if (i + 1 != argc) argp->parse(key, argv[++i], this);
else {
err_code = handle_missing(0, argv[i]);
goto error;
}
}
}
// parse previous arguments if IN_ORDER is not set
for (const auto arg : args) {
argp->parse(Key::ARG, arg, this);
}
// parse rest argv as normal arguments
for (i = i + 1; i < argc; i++) {
argp->parse(Key::ARG, argv[i], this);
arg_cnt++;
}
if (!arg_cnt) argp->parse(Key::NO_ARGS, 0, this);
argp->parse(Key::END, 0, this);
argp->parse(Key::SUCCESS, 0, this);
return 0;
error:
return err_code;
}
int Parser::handle_unknown(bool shrt, const char *argv) {
if (m_flags & NO_ERRS) return argp->parse(Key::ERROR, 0, this);
static const char *const unknown_fmt[2] = {
"unrecognized option '-%s'\n",
"invalid option -- '%s'\n",
};
failure(this, 1, 0, unknown_fmt[shrt], argv + 1);
see(stderr);
if (m_flags & NO_EXIT) return 1;
exit(1);
}
int Parser::handle_missing(bool shrt, const char *argv) {
if (m_flags & NO_ERRS) return argp->parse(Key::ERROR, 0, this);
static const char *const missing_fmt[2] = {
"option '-%s' requires an argument\n",
"option requires an argument -- '%s'\n",
};
failure(this, 2, 0, missing_fmt[shrt], argv + 1);
see(stderr);
if (m_flags & NO_EXIT) return 2;
exit(2);
}
int Parser::handle_excess(const char *argv) {
if (m_flags & NO_ERRS) return argp->parse(Key::ERROR, 0, this);
failure(this, 3, 0, "option '%s' doesn't allow an argument\n", argv);
see(stderr);
if (m_flags & NO_EXIT) return 3;
exit(3);
}
const char *Parser::basename(const char *name) {
const char *name_sh = std::strrchr(name, '/');
return name_sh ? name_sh + 1 : name;
}
} // namespace args

diff --git a/src/trie.cpp b/src/trie.cpp

@@ -1,8 +1,8 @@

#include "args.hpp"
#include "poafloc.hpp"
#include <cstdint>
namespace args {
namespace poafloc {
Parser::trie_t::~trie_t() noexcept {
for (uint8_t i = 0; i < 26; i++) {