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 |

commit8950af9d55d13335e71fe92c0cfc6ee132919584
parent130362e87a569b184f3dfef590569354de92397a
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateMon, 10 Jun 2024 21:23:26 +0200

Proof of concept C bindings

Diffstat:
MCMakeLists.txt|++++++--
Mdemo/CMakeLists.txt|+++++++++++-----
Ademo/main.c|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/args.h|++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/args.hpp|+++++++++++++--------------------------
Msrc/CMakeLists.txt|+++++++++++++++++-
Asrc/c_bindings.cpp|++++++++

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; }