From a7dd6bde3bffd4dbb5fb0ae616512881422c6ca9 Mon Sep 17 00:00:00 2001 From: lohhiiccc <96543753+lohhiiccc@users.noreply.github.com> Date: Sun, 29 Mar 2026 03:27:41 +0200 Subject: [PATCH] feat: add cli_print_options with extensible arg type system --- example/main.c | 58 +++++++++++++++++++++++++++----------------------- include/cli.h | 30 +++++++++++++++----------- src/help.c | 34 +++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 39 deletions(-) create mode 100644 src/help.c diff --git a/example/main.c b/example/main.c index ae8a6c8..00ab410 100644 --- a/example/main.c +++ b/example/main.c @@ -16,17 +16,42 @@ struct greet_config { uint8_t flags; }; +/* Forward declarations */ +static int handle_help(const char *arg, void *cfg); +static int handle_version(const char *arg, void *cfg); +static int handle_verbose(const char *arg, void *cfg); +static int handle_name(const char *arg, void *cfg); +static int handle_count(const char *arg, void *cfg); +/* ------------------- */ + +/* Option table */ + +#define GREET_OPTIONS_LIST \ + X('h', "help", no_argument, handle_help, OPT_ARG_NONE, "Display this help") \ + X('V', "version", no_argument, handle_version, OPT_ARG_NONE, "Display version") \ + X('v', "verbose", no_argument, handle_verbose, OPT_ARG_NONE, "Verbose output") \ + X('n', "name", required_argument, handle_name, OPT_ARG_STRING, "Name to greet") \ + X('c', "count", required_argument, handle_count, OPT_ARG_UINT, "Number of greetings") + +#define X(s, l, a, h, t, d) + 1 +enum { GREET_NB_OPTS = 0 GREET_OPTIONS_LIST }; +#undef X + +#define X(s, l, a, h, t, d) + (1 + (a != no_argument ? 1 : 0)) +enum { GREET_OPTSTR_LEN = 1 GREET_OPTIONS_LIST }; +#undef X + +#define X(s, l, a, h, t, d) { s, l, a, h, t, d }, +static const struct option_descriptor g_opts[] = { GREET_OPTIONS_LIST }; +#undef X + /* Handlers */ static int handle_help(__unused const char *arg, __unused void *cfg) { - printf("Usage: greet [OPTIONS] ...\n\n"); - printf(" -h, --help Display this help\n"); - printf(" -V, --version Display version\n"); - printf(" -v, --verbose Verbose output\n"); - printf(" -n, --name NAME Name to greet (default: world)\n"); - printf(" -c, --count N Number of greetings (default: 1)\n"); + printf("Usage: greet [OPTIONS]\n\n"); + cli_print_options(g_opts, GREET_NB_OPTS, cli_arg_type_to_str); return CLI_EXIT_SUCCESS; } @@ -62,27 +87,6 @@ handle_count(const char *arg, void *cfg) return CLI_SUCCESS; } -/* Option table */ - -#define GREET_OPTIONS_LIST \ - X('h', "help", no_argument, handle_help, OPT_ARG_NONE, "Display this help") \ - X('V', "version", no_argument, handle_version, OPT_ARG_NONE, "Display version") \ - X('v', "verbose", no_argument, handle_verbose, OPT_ARG_NONE, "Verbose output") \ - X('n', "name", required_argument, handle_name, OPT_ARG_STRING, "Name to greet") \ - X('c', "count", required_argument, handle_count, OPT_ARG_UINT, "Number of greetings") - -#define X(s, l, a, h, t, d) + 1 -enum { GREET_NB_OPTS = 0 GREET_OPTIONS_LIST }; -#undef X - -#define X(s, l, a, h, t, d) + (1 + (a != no_argument ? 1 : 0)) -enum { GREET_OPTSTR_LEN = 1 GREET_OPTIONS_LIST }; -#undef X - -#define X(s, l, a, h, t, d) { s, l, a, h, t, d }, -static const struct option_descriptor g_opts[] = { GREET_OPTIONS_LIST }; -#undef X - /* Main */ int diff --git a/include/cli.h b/include/cli.h index c85318a..cc42337 100644 --- a/include/cli.h +++ b/include/cli.h @@ -11,17 +11,7 @@ enum cli_code { }; typedef int (*t_option_handler)(const char *arg, void *config); - -enum option_arg_type -{ - OPT_ARG_NONE = 0, - OPT_ARG_INT, - OPT_ARG_UINT, - OPT_ARG_SECONDS, - OPT_ARG_BYTES, - OPT_ARG_TTL, - OPT_ARG_STRING -}; +typedef const char *(*t_arg_type_to_str)(int type); struct option_descriptor { @@ -29,11 +19,27 @@ struct option_descriptor const char *long_opt; int has_arg; t_option_handler handler; - enum option_arg_type arg_type; + int arg_type; const char *description; }; +/* Default arg types — clients may define their own */ +#define CLI_OPT_ARG_TYPES \ + X(OPT_ARG_NONE, "" ) \ + X(OPT_ARG_INT, "") \ + X(OPT_ARG_UINT, "") \ + X(OPT_ARG_FLOAT, "") \ + X(OPT_ARG_STRING, "") + +#define X(name, str) name, +enum { CLI_OPT_ARG_TYPES }; +#undef X + void cli_set_prog_name(const char *name); +void cli_print_options(const struct option_descriptor *opts, size_t nb_opts, + t_arg_type_to_str to_str); + +const char *cli_arg_type_to_str(int type); enum cli_code cli_parse(int argc, char **argv, void *config, const struct option_descriptor *opts, size_t nb_opts, diff --git a/src/help.c b/src/help.c new file mode 100644 index 0000000..3d1ef96 --- /dev/null +++ b/src/help.c @@ -0,0 +1,34 @@ +#include +#include + +#include "cli.h" +#include "compiler.h" + +const char * +cli_arg_type_to_str(int type) +{ + static const char *strs[] = { + #define X(name, str) str, + CLI_OPT_ARG_TYPES + #undef X + }; + if ((size_t)type < COUNT_OF(strs)) + return strs[type]; + return ""; +} + +void +cli_print_options(const struct option_descriptor *opts, size_t nb_opts, + t_arg_type_to_str to_str) +{ + printf("Options:\n"); + for (size_t i = 0; i < nb_opts; i++) + { + const char *argstr = to_str(opts[i].arg_type); + printf(" -%c, --%-15s %-8s %s\n", + opts[i].short_opt, + opts[i].long_opt, + argstr, + opts[i].description); + } +}