poafloc

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

commit 8950af9d55d13335e71fe92c0cfc6ee132919584
parent 130362e87a569b184f3dfef590569354de92397a
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Mon, 10 Jun 2024 23:23:26 +0200

Proof of concept C bindings

Diffstat:
MCMakeLists.txt | 8++++++--
Mdemo/CMakeLists.txt | 16+++++++++++-----
Ademo/main.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/args.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/args.hpp | 39+++++++++++++--------------------------
Msrc/CMakeLists.txt | 18+++++++++++++++++-
Asrc/c_bindings.cpp | 8++++++++
7 files changed, 191 insertions(+), 34 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,14 +3,18 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( args - VERSION 0.0.1 + VERSION 0.0.2 DESCRIPTION "Command Line Argument Parser" - LANGUAGES CXX + LANGUAGES CXX C ) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + add_subdirectory(src) add_subdirectory(demo) diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt @@ -1,13 +1,19 @@ set(GENERATE_OUT "${CMAKE_BINARY_DIR}/bin") -add_executable(demo - main.cpp -) - +add_executable(demo main.cpp) target_link_libraries(demo PRIVATE args) - +set_target_properties(demo PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(demo PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} RUNTIME_OUTPUT_DIRECTORY "${GENERATE_OUT}" ) + +add_executable(cdemo main.c) +target_link_libraries(cdemo PRIVATE cargs) +set_target_properties(cdemo PROPERTIES LINKER_LANGUAGE C) +set_target_properties(cdemo PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + RUNTIME_OUTPUT_DIRECTORY "${GENERATE_OUT}" +) diff --git a/demo/main.c b/demo/main.c @@ -0,0 +1,82 @@ +#include "args.h" + +#include <stdio.h> + +void error(const char *message) { fprintf(stderr, "%s\n", message); } + +typedef struct { + const char *output_file; + const char *input_file; + + int debug; + int hex; + int relocatable; +} arguments_t; + +int parse_opt(int key, const char *arg, args_parser *parser) { + arguments_t *arguments = (arguments_t *)args_parser_input(parser); + + switch (key) { + case 777: arguments->debug = 1; break; + case 'h': + if (arguments->relocatable) error("cannot mix -hex and -relocatable"); + arguments->hex = 1; + break; + case 'r': + if (arguments->hex) error("cannot mix -hex and -relocatable"); + arguments->relocatable = 1; + break; + 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: + arguments->input_file = "stdin"; + arguments->output_file = "stdout"; + } + + return 0; +} + +// clang-format off +static const args_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}, + { 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"}, + { 0, 'i', "file", 0, "Input file"}, + { 0, 0, 0, 0, "Informational Options", -1}, + {0}, +}; +// clang-format on + +int main(int argc, char *argv[]) { + arguments_t arguments = {0}; + args_argp_t argp = { + options, parse_opt, "doc string\nother usage", + "First half of the message\vsecond half of the message"}; + + if (args_parse(&argp, argc, argv, &arguments)) { + error("There was an error while parsing arguments"); + return 1; + } + + printf("Command line options:\n"); + printf("\t input: %s\n", arguments.input_file); + printf("\t output: %s\n", arguments.output_file); + printf("\t hex: %d\n", arguments.hex); + printf("\t debug: %d\n", arguments.debug); + printf("\t relocatable: %d\n", arguments.relocatable); + + // std::cout << "\t args: "; + // for (const auto &arg : arguments.args) + // std::cout << arg << " "; + // std::cout << std::endl; + + return 0; +} diff --git a/include/args.h b/include/args.h @@ -0,0 +1,54 @@ +#ifndef ARGS_H +#define ARGS_H + +#ifdef __cplusplus +extern "C" { +struct Parser; +typedef Parser args_parser; +#else +struct __Parser; +typedef struct __Parser args_parser; +#endif + +typedef struct { + const char *name; + int key; + const char *arg; + int flags; + const char *message; + int group; +} args_option_t; + +enum args_option_e { + ARGS_OPTION_ARG_OPTIONAL = 0x1, + ARGS_OPTION_HIDDEN = 0x2, + ARGS_OPTION_ALIAS = 0x4, +}; + +enum args_key_e { + ARGS_KEY_ARG = 0, + ARGS_KEY_END = 0x1000001, + ARGS_KEY_NO_ARGS = 0x1000002, + ARGS_KEY_INIT = 0x1000003, + ARGS_KEY_SUCCESS = 0x1000004, + ARGS_KEY_ERROR = 0x1000005, +}; + +struct args_Parser; + +typedef int (*args_parse_f)(int key, const char *arg, args_parser *parser); +typedef struct { + const args_option_t *options; + const args_parse_f parse; + const char *doc; + const char *message; +} args_argp_t; + +int args_parse(args_argp_t *argp, int argc, char *argv[], void *input); +void *args_parser_input(args_parser *parser); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/include/args.hpp b/include/args.hpp @@ -1,43 +1,30 @@ #ifndef ARGS_HPP #define ARGS_HPP +#include "args.h" + #include <string> #include <unordered_map> #include <vector> class Parser { public: - struct option_t { - const char *name; - const int key; - const char *arg; - const int flags; - const char *message; - const int group; - }; + using option_t = args_option_t; + using argp_t = args_argp_t; enum Option { - ARG_OPTIONAL = 0x1, - HIDDEN = 0x2, - ALIAS = 0x4, + ARG_OPTIONAL = ARGS_OPTION_ARG_OPTIONAL, + HIDDEN = ARGS_OPTION_HIDDEN, + ALIAS = ARGS_OPTION_ALIAS, }; enum Key { - ARG = 0, - END = 0x1000001, - NO_ARGS = 0x1000002, - INIT = 0x1000003, - SUCCESS = 0x1000004, - ERROR = 0x1000005, - }; - - struct argp_t { - using parse_f = int (*)(int key, const char *arg, Parser *parser); - - const option_t *options; - const parse_f parse; - const char *doc; - const char *message; + ARG = ARGS_KEY_ARG, + END = ARGS_KEY_END, + NO_ARGS = ARGS_KEY_NO_ARGS, + INIT = ARGS_KEY_INIT, + SUCCESS = ARGS_KEY_SUCCESS, + ERROR = ARGS_KEY_ERROR, }; static int parse(argp_t *argp, int argc, char *argv[], void *input) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -1,5 +1,6 @@ -add_library(args args.cpp help.cpp trie.cpp) +add_library(args STATIC args.cpp help.cpp trie.cpp) target_include_directories(args PUBLIC ../include) +set_target_properties(args PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(args PROPERTIES VERSION ${PROJECT_VERSION} @@ -11,3 +12,18 @@ install(TARGETS args LIBRARY DESTINATION lib PUBLIC_HEADER DESTINATION include ) + + add_library(cargs STATIC c_bindings.cpp args.cpp help.cpp trie.cpp) +target_include_directories(cargs PUBLIC ../include) +set_target_properties(cargs PROPERTIES LINKER_LANGUAGE CXX) + +set_target_properties(cargs PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + PUBLIC_HEADER ../include/args.h +) + +install(TARGETS cargs + LIBRARY DESTINATION lib + PUBLIC_HEADER DESTINATION include +) diff --git a/src/c_bindings.cpp b/src/c_bindings.cpp @@ -0,0 +1,8 @@ +#include "args.h" +#include "args.hpp" + +int args_parse(args_argp_t *argp, int argc, char *argv[], void *input) { + return Parser::parse(argp, argc, argv, input); +} + +void *args_parser_input(args_parser *parser) { return parser->input; }