poafloc

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

commit 31ee3b7c839842a6969085a8c52d9a83f225457b
parent 08b98eba297b9146c8a8aecacb5a50833df2d4bf
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Fri, 14 Jun 2024 19:23:08 +0200

Rebrand to poafloc

Diffstat:
MCMakeLists.txt | 26+++++++++++++-------------
DargsConfig.cmake.in | 6------
Mdemo/CMakeLists.txt | 4++--
Mdemo/main.c | 20++++++++++----------
Mdemo/main.cpp | 6+++---
Dinclude/args.h | 122-------------------------------------------------------------------------------
Dinclude/args.hpp | 111-------------------------------------------------------------------------------
Ainclude/poafloc.h | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/poafloc.hpp | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ApoaflocConfig.cmake.in | 6++++++
Msrc/CMakeLists.txt | 14+++++++-------
Dsrc/args.cpp | 287-------------------------------------------------------------------------------
Msrc/c_bindings.cpp | 16++++++++--------
Msrc/help.cpp | 4++--
Asrc/poafloc.cpp | 287+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/trie.cpp | 4++--
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++) {