diff options
author | Yaroslav de la Peña Smirnov <yps@yaroslavps.com> | 2025-09-12 23:43:49 +0300 |
---|---|---|
committer | Yaroslav de la Peña Smirnov <yps@yaroslavps.com> | 2025-09-12 23:43:49 +0300 |
commit | 0a8ee7a194e598d9c277b019503f7119b32bd17f (patch) | |
tree | 671a416c1fedec1235fa0fd8dce7fa6ed91095c3 | |
parent | 5a2a20eda7ca20bca2285e4940c4d1140301e8df (diff) | |
download | c-wares-0a8ee7a194e598d9c277b019503f7119b32bd17f.tar.gz c-wares-0a8ee7a194e598d9c277b019503f7119b32bd17f.zip |
cli: make it possible to extend option types
Make it possible to extend options with custom value parsers by embeding
`struct cli_opt` into a custom struct and setting the `set()` "method".
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | cli/cli-example.c | 75 | ||||
-rw-r--r-- | cli/cli-test.c | 244 | ||||
-rw-r--r-- | cli/cli.h | 251 |
4 files changed, 262 insertions, 309 deletions
@@ -4,3 +4,4 @@ build/ .gdb_history */*-test +*/*-example diff --git a/cli/cli-example.c b/cli/cli-example.c index 8149be0..941ddd6 100644 --- a/cli/cli-example.c +++ b/cli/cli-example.c @@ -3,67 +3,33 @@ */ #include "cli.h" -long distance; -unsigned long time; -long speed; +CLI_OPT_LONG(distance, 'd', "distance", "the distance (km/miles)"); +CLI_OPT_LONG(speed, 's', "speed", "the speed ((km/miles)ph)"); +CLI_OPT_LONG(time, 't', "time", "the time (seconds)"); +CLI_OPT_FLAG(murica, 'M', "murica", "Use American™ measurements"); -bool murica; - -int calc_speed(const struct cli_ctx *ctx) +int calc_speed(const struct cli_cmd *, const struct cli_ctx *) { - if (time == 0) { + if (time.value == 0) { fprintf(stderr, "time cannot be zero\n"); return CLI_RC_BAD_ARGS; } - double speed = (double)distance / ((double)time / 3600); - printf("%lf%s\n", speed, murica ? "mph" : "km/h"); + double speed = (double)distance.value / ((double)time.value / 3600); + printf("%lf%s\n", speed, murica.value ? "mph" : "km/h"); return CLI_RC_OK; } -int calc_distance(const struct cli_ctx *ctx) +int calc_distance(const struct cli_cmd *, const struct cli_ctx *) { - double distance = speed * ((double)time / 3600); - printf("%lf%s\n", distance, murica ? "miles" : "km"); + double distance = speed.value * ((double)time.value / 3600); + printf("%lf%s\n", distance, murica.value ? "miles" : "km"); return CLI_RC_OK; } -const struct cli_opt speed_opts[] = { - { - .type = CLI_OT_INT, - .shor = 'd', - .lon = "distance", - .desc = "the distance (km/miles)", - .value.i = &distance, - }, - { - .type = CLI_OT_UINT, - .shor = 't', - .lon = "time", - .desc = "the time (seconds)", - .value.u = &time, - }, - {0}, -}; - -const struct cli_opt distance_opts[] = { - { - .type = CLI_OT_INT, - .shor = 's', - .lon = "speed", - .desc = "the speed ((km/miles)ph)", - .value.i = &speed, - }, - { - .type = CLI_OT_UINT, - .shor = 't', - .lon = "time", - .desc = "the time (seconds)", - .value.u = &time, - }, - {0}, -}; +struct cli_opt *speed_opts[] = {&distance.opt, &time.opt, NULL}; +struct cli_opt *distance_opts[] = {&speed.opt, &time.opt, NULL}; const struct cli_cmd my_cmds[] = { { @@ -72,7 +38,7 @@ const struct cli_cmd my_cmds[] = { .help = "Tell me the distance and the time, and I'll tell you your speed", .opts = speed_opts, - .func = calc_speed, + .run = calc_speed, }, { .name = "distance", @@ -80,21 +46,12 @@ const struct cli_cmd my_cmds[] = { .help = "Tell me the speed and the time, and I'll tell you your distance", .opts = distance_opts, - .func = calc_distance, + .run = calc_distance, }, {0}, }; -struct cli_opt global_opts[] = { - { - .type = CLI_OT_FLAG, - .shor = 'M', - .lon = "murica", - .desc = "Use American™ measurements", - .value.f = &murica, - }, - {0}, -}; +struct cli_opt *global_opts[] = {&murica.opt, NULL}; struct cli my_cli = { .header = "This is an example CLI program", diff --git a/cli/cli-test.c b/cli/cli-test.c index 5af572c..c7a74e9 100644 --- a/cli/cli-test.c +++ b/cli/cli-test.c @@ -12,42 +12,49 @@ _s; \ }) +CLI_OPT_LONG(sopt, 's', "signed", NULL); +CLI_OPT_ULONG(uopt, 'u', "unsigned", NULL); +CLI_OPT_STRING(stropt, 'S', "string", NULL); +CLI_OPT_FLAG(aopt, 'a', "flag-a", NULL); +CLI_OPT_FLAG(bopt, 'b', "flag-b", NULL); +CLI_OPT_FLAG(copt, 'c', "flag-c", NULL); + +struct expected { + long i; + unsigned long u; + const char *s; + bool a; + bool b; + bool c; +}; + +struct cli_opt *options[] = { + &sopt.opt, + &uopt.opt, + &stropt.opt, + &aopt.opt, + &bopt.opt, + &copt.opt, + NULL, +}; + +static void reset_opts(void) +{ + sopt.value = 0; + uopt.value = 0; + stropt.value = NULL; + aopt.value = 0; + bopt.value = 0; + copt.value = 0; +} + TEST_BEGIN(test_parse_long) { - struct vars { - long i; - unsigned long u; - const char *s; - bool f; - } vars; - struct cli_opt options[] = { - { - .type = CLI_OT_INT, - .lon = "signed", - .value.i = &vars.i, - }, - { - .type = CLI_OT_FLAG, - .lon = "flag", - .value.f = &vars.f, - }, - { - .type = CLI_OT_UINT, - .lon = "unsigned", - .value.u = &vars.u, - }, - { - .type = CLI_OT_STRING, - .lon = "string", - .value.s = &vars.s, - }, - {0}, - }; struct tcase { - int argc; - char *argv[2]; - struct vars expected_vars; - enum cli_rc expected_rc; + int argc; + char *argv[2]; + struct expected expected_vars; + enum cli_rc expected_rc; } cases[] = { { .argc = 2, @@ -61,7 +68,8 @@ TEST_BEGIN(test_parse_long) .i = -2, .u = 0, .s = NULL, - .f = 0, + .a = false, + 0, }, .expected_rc = CLI_RC_OK, }, @@ -69,7 +77,7 @@ TEST_BEGIN(test_parse_long) .argc = 2, .argv = { - MUTSTR("--flag"), + MUTSTR("--flag-a"), MUTSTR("--unsigned=42"), }, .expected_vars = @@ -77,7 +85,8 @@ TEST_BEGIN(test_parse_long) .i = 0, .u = 0, .s = NULL, - .f = true, + .a = true, + 0, }, .expected_rc = CLI_RC_OK, }, @@ -92,7 +101,8 @@ TEST_BEGIN(test_parse_long) .i = 0, .u = 42, .s = NULL, - .f = false, + .a = false, + 0, }, .expected_rc = CLI_RC_OK, }, @@ -107,7 +117,8 @@ TEST_BEGIN(test_parse_long) .i = 0xB00B, .u = 0, .s = NULL, - .f = false, + .a = false, + 0, }, .expected_rc = CLI_RC_OK, }, @@ -123,7 +134,8 @@ TEST_BEGIN(test_parse_long) .i = 0, .u = 0, .s = "wololo", - .f = false, + .a = false, + 0, }, .expected_rc = CLI_RC_OK, }, @@ -138,7 +150,7 @@ TEST_BEGIN(test_parse_long) }, }; for (size_t i = 0; i < ARRAY_SIZE(cases); i++) { - vars = (struct vars){0}; + reset_opts(); struct cli_ctx ctx = { .opts = options, .argc = cases[i].argc, @@ -146,15 +158,15 @@ TEST_BEGIN(test_parse_long) }; int rc = parse_long(&ctx); asserteq(rc, cases[i].expected_rc); - asserteq(vars.i, cases[i].expected_vars.i); - asserteq(vars.u, cases[i].expected_vars.u); + asserteq(sopt.value, cases[i].expected_vars.i); + asserteq(uopt.value, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { - assertneq(vars.s, NULL); - asserteq(strcmp(vars.s, cases[i].expected_vars.s), 0); + assertneq(stropt.value, NULL); + asserteq(strcmp(stropt.value, cases[i].expected_vars.s), 0); } else { - asserteq(vars.s, NULL); + asserteq(stropt.value, NULL); } - asserteq(vars.f, cases[i].expected_vars.f); + asserteq(aopt.value, cases[i].expected_vars.a); } TEST_OUT } @@ -162,58 +174,17 @@ TEST_END TEST_BEGIN(test_parse_short) { - struct vars { - long i; - unsigned long u; - const char *s; - bool a; - bool b; - bool c; - } vars; - struct cli_opt options[] = { - { - .type = CLI_OT_INT, - .shor = 'i', - .value.i = &vars.i, - }, - { - .type = CLI_OT_FLAG, - .shor = 'a', - .value.f = &vars.a, - }, - { - .type = CLI_OT_FLAG, - .shor = 'b', - .value.f = &vars.b, - }, - { - .type = CLI_OT_FLAG, - .shor = 'c', - .value.f = &vars.c, - }, - { - .type = CLI_OT_UINT, - .shor = 'u', - .value.u = &vars.u, - }, - { - .type = CLI_OT_STRING, - .shor = 's', - .value.s = &vars.s, - }, - {0}, - }; struct tcase { - int argc; - char *argv[2]; - struct vars expected_vars; - enum cli_rc expected_rc; + int argc; + char *argv[2]; + struct expected expected_vars; + enum cli_rc expected_rc; } cases[] = { { .argc = 2, .argv = { - MUTSTR("-i-2"), + MUTSTR("-s-2"), MUTSTR("-b"), }, .expected_vars = @@ -248,7 +219,7 @@ TEST_BEGIN(test_parse_short) .argc = 1, .argv = { - MUTSTR("-sab"), + MUTSTR("-Sab"), }, .expected_vars = { @@ -299,7 +270,7 @@ TEST_BEGIN(test_parse_short) }, }; for (size_t i = 0; i < ARRAY_SIZE(cases); i++) { - vars = (struct vars){0}; + reset_opts(); struct cli_ctx ctx = { .opts = options, .argc = cases[i].argc, @@ -307,17 +278,15 @@ TEST_BEGIN(test_parse_short) }; int rc = parse_short(&ctx); asserteq(rc, cases[i].expected_rc); - asserteq(vars.i, cases[i].expected_vars.i); - asserteq(vars.u, cases[i].expected_vars.u); + asserteq(sopt.value, cases[i].expected_vars.i); + asserteq(uopt.value, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { - assertneq(vars.s, NULL); - asserteq(strcmp(vars.s, cases[i].expected_vars.s), 0); + assertneq(stropt.value, NULL); + asserteq(strcmp(stropt.value, cases[i].expected_vars.s), 0); } else { - asserteq(vars.s, NULL); + asserteq(stropt.value, NULL); } - asserteq(vars.a, cases[i].expected_vars.a); - asserteq(vars.b, cases[i].expected_vars.b); - asserteq(vars.c, cases[i].expected_vars.c); + asserteq(aopt.value, cases[i].expected_vars.a); } TEST_OUT } @@ -325,58 +294,11 @@ TEST_END TEST_BEGIN(test_parse_options) { - struct vars { - long i; - unsigned long u; - const char *s; - bool a; - bool b; - bool c; - } vars; - struct cli_opt options[] = { - { - .type = CLI_OT_INT, - .lon = "signed", - .shor = 's', - .value.i = &vars.i, - }, - { - .type = CLI_OT_FLAG, - .lon = "flag-a", - .shor = 'a', - .value.f = &vars.a, - }, - { - .type = CLI_OT_FLAG, - .lon = "flag-b", - .shor = 'b', - .value.f = &vars.b, - }, - { - .type = CLI_OT_FLAG, - .lon = "flag-c", - .shor = 'c', - .value.f = &vars.c, - }, - { - .type = CLI_OT_UINT, - .lon = "unsigned", - .shor = 'u', - .value.u = &vars.u, - }, - { - .type = CLI_OT_STRING, - .lon = "string", - .shor = 'S', - .value.s = &vars.s, - }, - {0}, - }; struct tcase { - int argc; - char *argv[8]; - struct vars expected_vars; - enum cli_rc expected_rc; + int argc; + char *argv[8]; + struct expected expected_vars; + enum cli_rc expected_rc; } cases[] = { { .argc = 5, @@ -497,7 +419,7 @@ TEST_BEGIN(test_parse_options) }, }; for (size_t i = 0; i < ARRAY_SIZE(cases); i++) { - vars = (struct vars){0}; + reset_opts(); struct cli_ctx ctx = { .opts = options, .argc = cases[i].argc, @@ -505,17 +427,15 @@ TEST_BEGIN(test_parse_options) }; int rc = parse_options(&ctx); asserteq(rc, cases[i].expected_rc); - asserteq(vars.i, cases[i].expected_vars.i); - asserteq(vars.u, cases[i].expected_vars.u); + asserteq(sopt.value, cases[i].expected_vars.i); + asserteq(uopt.value, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { - assertneq(vars.s, NULL); - asserteq(strcmp(vars.s, cases[i].expected_vars.s), 0); + assertneq(stropt.value, NULL); + asserteq(strcmp(stropt.value, cases[i].expected_vars.s), 0); } else { - asserteq(vars.s, NULL); + asserteq(stropt.value, NULL); } - asserteq(vars.a, cases[i].expected_vars.a); - asserteq(vars.b, cases[i].expected_vars.b); - asserteq(vars.c, cases[i].expected_vars.c); + asserteq(aopt.value, cases[i].expected_vars.a); } TEST_OUT } @@ -13,6 +13,9 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <inttypes.h> + +#include "../utils.h" /** * enum cli_rc - Return codes for CLI parsing results. @@ -40,24 +43,21 @@ enum cli_rc { * @argv: the array of command line arguments passed on to the command. */ struct cli_ctx { - const struct cli_opt *opts; - int argc; - char *const *argv; + struct cli_opt **opts; + int argc; + char *const *argv; }; /** * enum cli_opt_type - The type of option. - * @__CLI_OT_NONE: used as sentinel value; shouldn't be used directly. - * @CLI_OT_INT: an integer; will be parsed as a C `long`. - * @CLI_OT_STRING: a string; the pointer to the string will be passed as is. - * @CLI_OT_FLAG: a flag; if the key was given, it wil be set; has no value. + * @__CLI_OT_NONE: unused. + * @CLI_OT_FLAG: a flag option; has no value. + * @CLI_OT_VALUE: a value option; it expects a value after the option key. */ -enum cli_opt_type { +enum cli_opt_type : uint8_t { __CLI_OT_NONE, - CLI_OT_INT, - CLI_OT_UINT, - CLI_OT_STRING, CLI_OT_FLAG, + CLI_OT_VALUE, }; /** @@ -66,27 +66,125 @@ enum cli_opt_type { * @shor: short key, e.g. 'k' for "-k". * @lon: long key, e.g. "key" for "--key". * @desc: a short description of the option to be displayed in the help. - * @value: pointer to variable to set: - * - STRING: @value.s, will be set to point to the argv with the - * value. - * - INT: @value.i, will be set to the parsed value of the argv. - * - UINT: @value.u, same as INT but unsigned - * - FLAG: @value.f, will be set to true upon encountering the - * option. */ struct cli_opt { enum cli_opt_type type; char shor; const char *lon; const char *desc; - union { - long *i; - unsigned long *u; - const char **s; - bool *f; - } value; + int (*set)(struct cli_opt *self, const char *val); +}; + +struct cli_opt_long { + struct cli_opt opt; + long value; +}; + +#define CLI_OPT_LONG(name, sh, ln, dsc) \ + struct cli_opt_long name = { \ + .opt = \ + { \ + .type = CLI_OT_VALUE, \ + .shor = sh, \ + .lon = ln, \ + .desc = dsc, \ + .set = cli_opt_long_set, \ + }, \ + } + +int cli_opt_long_set(struct cli_opt *self, const char *val) +{ + struct cli_opt_long *opt = container_of(self, struct cli_opt_long, opt); + + errno = 0; + opt->value = strtol(val, NULL, 0); + if (errno != 0) + return CLI_RC_BAD_ARGS; + + return CLI_RC_OK; +} + +struct cli_opt_ulong { + struct cli_opt opt; + long value; +}; + +#define CLI_OPT_ULONG(name, sh, ln, dsc) \ + struct cli_opt_ulong name = { \ + .opt = \ + { \ + .type = CLI_OT_VALUE, \ + .shor = sh, \ + .lon = ln, \ + .desc = dsc, \ + .set = cli_opt_ulong_set, \ + }, \ + } + +int cli_opt_ulong_set(struct cli_opt *self, const char *val) +{ + struct cli_opt_ulong *opt = container_of(self, struct cli_opt_ulong, opt); + + errno = 0; + opt->value = strtoul(val, NULL, 0); + if (errno != 0) + return CLI_RC_BAD_ARGS; + + return CLI_RC_OK; +} + +struct cli_opt_string { + struct cli_opt opt; + const char *value; +}; + +#define CLI_OPT_STRING(name, sh, ln, dsc) \ + struct cli_opt_string name = { \ + .opt = \ + { \ + .type = CLI_OT_VALUE, \ + .shor = sh, \ + .lon = ln, \ + .desc = dsc, \ + .set = cli_opt_string_set, \ + }, \ + } + +int cli_opt_string_set(struct cli_opt *self, const char *val) +{ + struct cli_opt_string *opt = container_of(self, struct cli_opt_string, opt); + + if (!val) + return CLI_RC_BAD_ARGS; + opt->value = val; + + return CLI_RC_OK; +} + +struct cli_opt_flag { + struct cli_opt opt; + bool value; }; +#define CLI_OPT_FLAG(name, sh, ln, dsc) \ + struct cli_opt_flag name = { \ + .opt = \ + { \ + .type = CLI_OT_FLAG, \ + .shor = sh, \ + .lon = ln, \ + .desc = dsc, \ + .set = cli_opt_flag_set, \ + }, \ + } + +int cli_opt_flag_set(struct cli_opt *self, const char *) +{ + struct cli_opt_flag *opt = container_of(self, struct cli_opt_flag, opt); + opt->value = true; + return CLI_RC_OK; +} + /** * struct cli_cmd - Represents a single command for the CLI. * @name: the command name as it should be read from argv. @@ -98,17 +196,17 @@ struct cli_opt { * they are read from the arguments. cli.h will stop parsing once <--> * is encountered, or a value (non-option arg) without key is read. * The remaining args will be passed in cli_cmd_ctx. - * @func: the function to execute when the command is invoked; the options - * that were passed in this struct will be filled according to what is + * @run: the function to execute when the command is invoked; the options + * that were passed in this struct will be set according to what is * parsed from the command-line arguments. */ struct cli_cmd { - const char *name; - const char *desc; - const char *usage; - const char *help; - const struct cli_opt *opts; - int (*func)(const struct cli_ctx *ctx); + const char *name; + const char *desc; + const char *usage; + const char *help; + struct cli_opt **opts; + int (*run)(const struct cli_cmd *self, const struct cli_ctx *ctx); }; /** @@ -124,7 +222,7 @@ struct cli { const char *header; const char *footer; const struct cli_cmd *cmds; - const struct cli_opt *opts; + struct cli_opt **opts; }; static inline bool arg_is_help(const char *const arg) @@ -132,9 +230,10 @@ static inline bool arg_is_help(const char *const arg) return !strcmp(arg, "--help") || !strcmp(arg, "help") || !strcmp(arg, "-h"); } -static void list_options(const struct cli_opt *opts) +static void list_options(struct cli_opt **opts) { - for (const struct cli_opt *opt = opts; opt->type; opt++) { + for (size_t i = 0; opts[i]; i++) { + const struct cli_opt *opt = opts[i]; if (opt->lon) { if (opt->shor) { printf("\t--%s, -%c\t", opt->lon, opt->shor); @@ -155,7 +254,8 @@ static void explain_program(const struct cli *const cli) printf("%s\n", cli->header); printf("Usage: %s%s <command> [command options]\n\n", - cli->opts ? "[global options] " : "", cli->binary); + cli->opts ? "[global options] " : "", + cli->binary); if (cli->cmds) { puts("Commands:"); @@ -183,8 +283,11 @@ static void explain_command(const struct cli *const cli, if (cli->header) printf("%s\n", cli->header); - printf("Usage: %s%s %s %s\n\n", cli->opts ? "[global options] " : "", - cli->binary, cmd->name, cmd->usage ?: "[options]"); + printf("Usage: %s%s %s %s\n\n", + cli->opts ? "[global options] " : "", + cli->binary, + cmd->name, + cmd->usage ?: "[options]"); printf("%s\n\n", cmd->help); if (cmd->opts) { @@ -196,35 +299,6 @@ static void explain_command(const struct cli *const cli, printf("%s\n", cli->footer); } -static int parse_value(const struct cli_opt *opt, const char *value) -{ - switch (opt->type) { - case CLI_OT_INT: - errno = 0; - *opt->value.i = strtol(value, NULL, 0); - if (errno != 0) - return CLI_RC_BAD_ARGS; - break; - case CLI_OT_UINT: - errno = 0; - *opt->value.u = strtoul(value, NULL, 0); - if (errno != 0) - return CLI_RC_BAD_ARGS; - break; - case CLI_OT_STRING: - *opt->value.s = value; - break; - case CLI_OT_FLAG: - *opt->value.f = true; - break; - default: - fprintf(stderr, "BUG: incorrect option type %d\n", opt->type); - return CLI_RC_BUG; - } - - return CLI_RC_OK; -} - static int parse_long(struct cli_ctx *ctx) { char *key = ctx->argv[0] + 2; @@ -238,15 +312,15 @@ static int parse_long(struct cli_ctx *ctx) } } - const struct cli_opt *opt = ctx->opts; - if (opt) { - for (; opt->type; opt++) { - if (!strcmp(key, opt->lon)) - break; + struct cli_opt *opt = NULL; + for (size_t i = 0; ctx->opts[i]; i++) { + if (!strcmp(key, ctx->opts[i]->lon)) { + opt = ctx->opts[i]; + break; } } - if (!opt || !opt->type) { + if (!opt) { fprintf(stderr, "bad long option '--%s'\n", key); return CLI_RC_BAD_ARGS; } @@ -263,7 +337,7 @@ static int parse_long(struct cli_ctx *ctx) value = *ctx->argv; } - int rc = parse_value(opt, value); + int rc = opt->set(opt, value); if (rc == CLI_RC_BAD_ARGS) fprintf(stderr, "bad value '%s' for option '--%s'\n", value, key); @@ -276,15 +350,15 @@ static int parse_long(struct cli_ctx *ctx) static int parse_short(struct cli_ctx *ctx) { for (const char *c = ctx->argv[0] + 1; *c != '\0'; c++) { - const struct cli_opt *opt = ctx->opts; - if (opt) { - for (; opt->type; opt++) { - if (*c == opt->shor) - break; + struct cli_opt *opt = NULL; + for (size_t i = 0; ctx->opts[i]; i++) { + if (*c == ctx->opts[i]->shor) { + opt = ctx->opts[i]; + break; } } - if (!opt || !opt->type) { + if (!opt) { fprintf(stderr, "bad short option '-%c'\n", *c); return CLI_RC_BAD_ARGS; } @@ -307,18 +381,18 @@ static int parse_short(struct cli_ctx *ctx) value = *ctx->argv; } - int rc = parse_value(opt, value); + int rc = opt->set(opt, value); if (rc != CLI_RC_OK) { if (rc == CLI_RC_BAD_ARGS) - fprintf(stderr, "bad value '%s' for option '-%c'\n", value, - *c); + fprintf( + stderr, "bad value '%s' for option '-%c'\n", value, *c); return rc; } goto finish; } - int rc = parse_value(opt, NULL); + int rc = opt->set(opt, NULL); if (rc != CLI_RC_OK) return rc; /* we might have several flags strung together, e.g. '-abcd' */ @@ -380,7 +454,7 @@ static inline int cli_cmd_run(const struct cli *const cli, goto out; } - rc = cmd->func(&cmd_ctx); + rc = cmd->run(cmd, &cmd_ctx); if (rc == CLI_RC_BAD_ARGS) goto help; @@ -399,6 +473,7 @@ out: * * Return: CLI_RC_OK on success, anything else on error. */ +[[maybe_unused]] static int cli_run(struct cli *const cli, int argc, char *argv[]) { cli->binary = cli->binary ?: argv[0]; @@ -419,9 +494,9 @@ static int cli_run(struct cli *const cli, int argc, char *argv[]) for (const struct cli_cmd *cmd = cli->cmds; cmd->name; cmd++) { if (!strcmp(cmd->name, ctx.argv[0])) { - if (!cmd->func) { - fprintf(stderr, "BUG: command `%s` has no function\n", - cmd->name); + if (!cmd->run) { + fprintf( + stderr, "BUG: command `%s` has no function\n", cmd->name); return CLI_RC_BUG; } |