feat: add cli_print_options with extensible arg type system

This commit is contained in:
lohhiiccc 2026-03-29 03:27:41 +02:00
parent 3336ac272c
commit a7dd6bde3b
3 changed files with 83 additions and 39 deletions

View file

@ -16,17 +16,42 @@ struct greet_config {
uint8_t flags; 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 */ /* Handlers */
static int static int
handle_help(__unused const char *arg, __unused void *cfg) handle_help(__unused const char *arg, __unused void *cfg)
{ {
printf("Usage: greet [OPTIONS] ...\n\n"); printf("Usage: greet [OPTIONS]\n\n");
printf(" -h, --help Display this help\n"); cli_print_options(g_opts, GREET_NB_OPTS, cli_arg_type_to_str);
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");
return CLI_EXIT_SUCCESS; return CLI_EXIT_SUCCESS;
} }
@ -62,27 +87,6 @@ handle_count(const char *arg, void *cfg)
return CLI_SUCCESS; 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 */ /* Main */
int int

View file

@ -11,17 +11,7 @@ enum cli_code {
}; };
typedef int (*t_option_handler)(const char *arg, void *config); typedef int (*t_option_handler)(const char *arg, void *config);
typedef const char *(*t_arg_type_to_str)(int type);
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
};
struct option_descriptor struct option_descriptor
{ {
@ -29,11 +19,27 @@ struct option_descriptor
const char *long_opt; const char *long_opt;
int has_arg; int has_arg;
t_option_handler handler; t_option_handler handler;
enum option_arg_type arg_type; int arg_type;
const char *description; const char *description;
}; };
/* Default arg types — clients may define their own */
#define CLI_OPT_ARG_TYPES \
X(OPT_ARG_NONE, "" ) \
X(OPT_ARG_INT, "<NUM>") \
X(OPT_ARG_UINT, "<NUM>") \
X(OPT_ARG_FLOAT, "<NUM>") \
X(OPT_ARG_STRING, "<STR>")
#define X(name, str) name,
enum { CLI_OPT_ARG_TYPES };
#undef X
void cli_set_prog_name(const char *name); 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, enum cli_code cli_parse(int argc, char **argv, void *config,
const struct option_descriptor *opts, size_t nb_opts, const struct option_descriptor *opts, size_t nb_opts,

34
src/help.c Normal file
View file

@ -0,0 +1,34 @@
#include <stdio.h>
#include <stddef.h>
#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 "<ARG>";
}
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);
}
}