From 783c36248987560c1abf07297bdd1a144d60b749 Mon Sep 17 00:00:00 2001 From: ej-shafran Date: Sun, 17 Sep 2023 01:27:15 +0300 Subject: [PATCH 1/4] Add alias support - Add `aliases` and `alias_count` fields to the `Flag` struct - Respect the aliases when parsing flags - Print out aliases when printing out options - Update example to feature this --- example.c | 6 ++--- flag.h | 71 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/example.c b/example.c index e53028b..72670f1 100644 --- a/example.c +++ b/example.c @@ -13,9 +13,9 @@ void usage(FILE *stream) int main(int argc, char **argv) { - bool *help = flag_bool("help", false, "Print this help to stdout and exit with 0"); - char **line = flag_str("line", "Hi!", "Line to output to the file"); - size_t *count = flag_size("count", 64, "Amount of lines to generate"); + bool *help = flag_bool_aliases("-help", false, "Print this help to stdout and exit with 0", "h"); + char **line = flag_str_aliases("-line", "Hi!", "Line to output to the file", "l"); + size_t *count = flag_size_aliases("-count", 64, "Amount of lines to generate", "c"); if (!flag_parse(argc, argv)) { usage(stderr); diff --git a/flag.h b/flag.h index 947dbff..7ac4978 100644 --- a/flag.h +++ b/flag.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -24,10 +25,22 @@ // WARNING! *_var functions may break the flag_name() functionality char *flag_name(void *val); -bool *flag_bool(const char *name, bool def, const char *desc); -uint64_t *flag_uint64(const char *name, uint64_t def, const char *desc); -size_t *flag_size(const char *name, uint64_t def, const char *desc); -char **flag_str(const char *name, const char *def, const char *desc); +bool *flag_bool_null(const char *name, bool def, const char *desc, ...); +#define flag_bool(name, def, desc) flag_bool_null(name, def, desc) +#define flag_bool_aliases(name, def, desc, ...) \ + flag_bool_null(name, def, desc, __VA_ARGS__, NULL) +uint64_t *flag_uint64_null(const char *name, uint64_t def, const char *desc, ...); +#define flag_uint64(name, def, desc) flag_uint64_null(name, def, desc, NULL) +#define flag_uint64_aliases(name, def, desc, ...) \ + flag_uint64_null(name, def, desc, __VA_ARGS__, NULL) +size_t *flag_size_null(const char *name, uint64_t def, const char *desc, ...); +#define flag_size(name, def, desc) flag_size_null(name, def, desc, NULL) +#define flag_size_aliases(name, def, desc, ...) \ + flag_size_null(name, def, desc, __VA_ARGS__, NULL) +char **flag_str_null(const char *name, const char *def, const char *desc, ...); +#define flag_str(name, def, desc) flag_str_null(name, def, desc, NULL) +#define flag_str_aliases(name, def, desc, ...) \ + flag_str_null(name, def, desc, __VA_ARGS__, NULL) bool flag_parse(int argc, char **argv); int flag_rest_argc(void); char **flag_rest_argv(void); @@ -66,9 +79,15 @@ typedef enum { COUNT_FLAG_ERRORS, } Flag_Error; +#ifndef ALIAS_CAP +#define ALIAS_CAP 12 +#endif // ALIAS_CAP + typedef struct { Flag_Type type; char *name; + char *aliases[ALIAS_CAP]; + size_t alias_count; char *desc; Flag_Value val; Flag_Value def; @@ -91,7 +110,7 @@ typedef struct { static Flag_Context flag_global_context; -Flag *flag_new(Flag_Type type, const char *name, const char *desc) +Flag *flag_new(Flag_Type type, const char *name, const char *desc, va_list aliases) { Flag_Context *c = &flag_global_context; @@ -102,6 +121,11 @@ Flag *flag_new(Flag_Type type, const char *name, const char *desc) // NOTE: I won't touch them I promise Kappa flag->name = (char*) name; flag->desc = (char*) desc; + char *alias = va_arg(aliases, char *); + while (alias != NULL) { + flag->aliases[flag->alias_count++] = alias; + alias = va_arg(aliases, char *); + } return flag; } @@ -111,33 +135,41 @@ char *flag_name(void *val) return flag->name; } -bool *flag_bool(const char *name, bool def, const char *desc) +bool *flag_bool_null(const char *name, bool def, const char *desc, ...) { - Flag *flag = flag_new(FLAG_BOOL, name, desc); + va_list aliases; + va_start(aliases, desc); + Flag *flag = flag_new(FLAG_BOOL, name, desc, aliases); flag->def.as_bool = def; flag->val.as_bool = def; return &flag->val.as_bool; } -uint64_t *flag_uint64(const char *name, uint64_t def, const char *desc) +uint64_t *flag_uint64_null(const char *name, uint64_t def, const char *desc, ...) { - Flag *flag = flag_new(FLAG_UINT64, name, desc); + va_list aliases; + va_start(aliases, desc); + Flag *flag = flag_new(FLAG_UINT64, name, desc, aliases); flag->val.as_uint64 = def; flag->def.as_uint64 = def; return &flag->val.as_uint64; } -size_t *flag_size(const char *name, uint64_t def, const char *desc) +size_t *flag_size_null(const char *name, uint64_t def, const char *desc, ...) { - Flag *flag = flag_new(FLAG_SIZE, name, desc); + va_list aliases; + va_start(aliases, desc); + Flag *flag = flag_new(FLAG_SIZE, name, desc, aliases); flag->val.as_size = def; flag->def.as_size = def; return &flag->val.as_size; } -char **flag_str(const char *name, const char *def, const char *desc) +char **flag_str_null(const char *name, const char *def, const char *desc, ...) { - Flag *flag = flag_new(FLAG_STR, name, desc); + va_list aliases; + va_start(aliases, desc); + Flag *flag = flag_new(FLAG_STR, name, desc, aliases); flag->val.as_str = (char*) def; flag->def.as_str = (char*) def; return &flag->val.as_str; @@ -190,7 +222,13 @@ bool flag_parse(int argc, char **argv) bool found = false; for (size_t i = 0; i < c->flags_count; ++i) { - if (strcmp(c->flags[i].name, flag) == 0) { + bool is_name = strcmp(c->flags[i].name, flag) == 0; + bool is_alias = false; + for (size_t j = 0; !is_alias && j < c->flags[i].alias_count; ++j) { + if (strcmp(c->flags[i].aliases[j], flag) == 0) + is_alias = true; + } + if (is_name || is_alias) { static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive flag type parsing"); switch (c->flags[i].type) { case FLAG_BOOL: { @@ -311,7 +349,10 @@ void flag_print_options(FILE *stream) for (size_t i = 0; i < c->flags_count; ++i) { Flag *flag = &c->flags[i]; - fprintf(stream, " -%s\n", flag->name); + fprintf(stream, " -%s", flag->name); + for (size_t j = 0; j < flag->alias_count; ++j) + fprintf(stream, ", -%s", flag->aliases[j]); + fprintf(stream, "\n"); fprintf(stream, " %s\n", flag->desc); static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive flag type defaults printing"); switch (c->flags[i].type) { From ddf7e7f947484e9ddfb45275812242168ea3e79b Mon Sep 17 00:00:00 2001 From: ej-shafran Date: Sun, 17 Sep 2023 02:19:16 +0300 Subject: [PATCH 2/4] Fix missing `NULL` for `flag_bool` --- flag.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flag.h b/flag.h index 7ac4978..9afd3fb 100644 --- a/flag.h +++ b/flag.h @@ -26,7 +26,7 @@ char *flag_name(void *val); bool *flag_bool_null(const char *name, bool def, const char *desc, ...); -#define flag_bool(name, def, desc) flag_bool_null(name, def, desc) +#define flag_bool(name, def, desc) flag_bool_null(name, def, desc, NULL) #define flag_bool_aliases(name, def, desc, ...) \ flag_bool_null(name, def, desc, __VA_ARGS__, NULL) uint64_t *flag_uint64_null(const char *name, uint64_t def, const char *desc, ...); From f8b65f4d569dd645129c2b20b0c763742d4b64b2 Mon Sep 17 00:00:00 2001 From: ej-shafran Date: Sun, 17 Sep 2023 02:19:45 +0300 Subject: [PATCH 3/4] Add `flag_add_alias` macro Similar to `flag_name` (and might also break if `*_var` style functions are introduced) --- example.c | 10 +++++++--- flag.h | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/example.c b/example.c index 72670f1..a875983 100644 --- a/example.c +++ b/example.c @@ -13,9 +13,13 @@ void usage(FILE *stream) int main(int argc, char **argv) { - bool *help = flag_bool_aliases("-help", false, "Print this help to stdout and exit with 0", "h"); - char **line = flag_str_aliases("-line", "Hi!", "Line to output to the file", "l"); - size_t *count = flag_size_aliases("-count", 64, "Amount of lines to generate", "c"); + bool *help = flag_bool("-help", false, "Print this help to stdout and exit with 0"); + char **line = flag_str("-line", "Hi!", "Line to output to the file"); + size_t *count = flag_size("-count", 64, "Amount of lines to generate"); + + flag_add_alias(help, "h"); + flag_add_alias(line, "l"); + flag_add_alias(count, "c"); if (!flag_parse(argc, argv)) { usage(stderr); diff --git a/flag.h b/flag.h index 9afd3fb..8a890c3 100644 --- a/flag.h +++ b/flag.h @@ -25,6 +25,7 @@ // WARNING! *_var functions may break the flag_name() functionality char *flag_name(void *val); +void flag_add_alias(void *val, const char *alias); bool *flag_bool_null(const char *name, bool def, const char *desc, ...); #define flag_bool(name, def, desc) flag_bool_null(name, def, desc, NULL) #define flag_bool_aliases(name, def, desc, ...) \ @@ -86,7 +87,7 @@ typedef enum { typedef struct { Flag_Type type; char *name; - char *aliases[ALIAS_CAP]; + const char *aliases[ALIAS_CAP]; size_t alias_count; char *desc; Flag_Value val; @@ -121,10 +122,11 @@ Flag *flag_new(Flag_Type type, const char *name, const char *desc, va_list alias // NOTE: I won't touch them I promise Kappa flag->name = (char*) name; flag->desc = (char*) desc; - char *alias = va_arg(aliases, char *); + const char *alias = va_arg(aliases, const char *); while (alias != NULL) { + assert(flag->alias_count <= ALIAS_CAP); flag->aliases[flag->alias_count++] = alias; - alias = va_arg(aliases, char *); + alias = va_arg(aliases, const char *); } return flag; } @@ -135,6 +137,13 @@ char *flag_name(void *val) return flag->name; } +void flag_add_alias(void *val, const char *alias) +{ + Flag *flag = (Flag*) ((char*) val - offsetof(Flag, val)); + assert(flag->alias_count + 1 <= ALIAS_CAP); + flag->aliases[flag->alias_count++] = alias; +} + bool *flag_bool_null(const char *name, bool def, const char *desc, ...) { va_list aliases; From 250d6c7de1837797e8a4c1f6ba9d2c6ec6428f35 Mon Sep 17 00:00:00 2001 From: ej-shafran Date: Sun, 17 Sep 2023 02:23:10 +0300 Subject: [PATCH 4/4] Tiny optimization Don't check for aliases if we know it's the flag's name already --- flag.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/flag.h b/flag.h index 8a890c3..c1cfe92 100644 --- a/flag.h +++ b/flag.h @@ -233,9 +233,11 @@ bool flag_parse(int argc, char **argv) for (size_t i = 0; i < c->flags_count; ++i) { bool is_name = strcmp(c->flags[i].name, flag) == 0; bool is_alias = false; - for (size_t j = 0; !is_alias && j < c->flags[i].alias_count; ++j) { - if (strcmp(c->flags[i].aliases[j], flag) == 0) - is_alias = true; + if (!is_name) { + for (size_t j = 0; !is_alias && j < c->flags[i].alias_count; ++j) { + if (strcmp(c->flags[i].aliases[j], flag) == 0) + is_alias = true; + } } if (is_name || is_alias) { static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive flag type parsing");