aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYaroslav de la Peña Smirnov <yps@yaroslavps.com>2025-09-12 23:43:49 +0300
committerYaroslav de la Peña Smirnov <yps@yaroslavps.com>2025-09-12 23:43:49 +0300
commit0a8ee7a194e598d9c277b019503f7119b32bd17f (patch)
tree671a416c1fedec1235fa0fd8dce7fa6ed91095c3
parent5a2a20eda7ca20bca2285e4940c4d1140301e8df (diff)
downloadc-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--.gitignore1
-rw-r--r--cli/cli-example.c75
-rw-r--r--cli/cli-test.c244
-rw-r--r--cli/cli.h251
4 files changed, 262 insertions, 309 deletions
diff --git a/.gitignore b/.gitignore
index af407b2..daf7b40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
}
diff --git a/cli/cli.h b/cli/cli.h
index f5ce8f3..07f0ab4 100644
--- a/cli/cli.h
+++ b/cli/cli.h
@@ -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;
}