From b481db6c20c848888b6c671bd7dc00d936b340d5 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Tue, 11 Aug 2020 22:07:32 -0600 Subject: [PATCH] multicall: add support for unified option handling --- cmd/multicall.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++- cmd/multicall.h | 23 ++++++++ 2 files changed, 164 insertions(+), 2 deletions(-) diff --git a/cmd/multicall.c b/cmd/multicall.c index c6ae3d4..65bd9e9 100644 --- a/cmd/multicall.c +++ b/cmd/multicall.c @@ -13,10 +13,12 @@ * from the use of this software. */ +#define _GNU_SOURCE #include #include #include #include +#include #include "cmd/multicall.h" char *argv0; @@ -33,7 +35,67 @@ struct if_applet *applet_table[] = { &ifupdown_applet, }; -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) +static void +generic_usage(const struct if_applet *applet, int result) +{ + fprintf(stderr, "%s", applet->name); + if (applet->desc != NULL) + fprintf(stderr, " - %s", applet->desc); + + fprintf(stderr, "\n"); + + size_t iter; + for (iter = 0; applet->groups[iter] != NULL; iter++) + { + const struct if_option_group *group = applet->groups[iter]; + + fprintf(stderr, "\n%s:\n", group->desc); + + size_t group_iter; + for (group_iter = 0; group_iter < group->group_size; group_iter++) + { + const struct if_option *opt = &group->group[group_iter]; + + fprintf(stderr, " "); + + if (opt->short_opt) + fprintf(stderr, "-%c", opt->short_opt); + else + fprintf(stderr, " "); + + if (opt->long_opt) + fprintf(stderr, "%c --%-30s", opt->short_opt ? ',' : ' ', + opt->long_opt); + else + fprintf(stderr, "%34s", ""); + + fprintf(stderr, "%s\n", opt->desc); + } + } + + exit(result); +} + +static void +generic_usage_request(int short_opt, const struct if_option *option, const char *opt_arg, const struct if_applet *applet) +{ + (void) short_opt; + (void) option; + (void) opt_arg; + + generic_usage(applet, EXIT_SUCCESS); +} + +static struct if_option global_options[] = { + {'h', "help", "displays program help", false, generic_usage_request}, + {'V', "version", "displays program version", false, (void *) lif_common_version}, +}; + +struct if_option_group global_option_group = { + .desc = "Global options", + .group_size = ARRAY_SIZE(global_options), + .group = global_options +}; int applet_cmp(const void *a, const void *b) @@ -46,11 +108,83 @@ applet_cmp(const void *a, const void *b) void multicall_usage(int status) __attribute__((noreturn)); +static const struct if_option * +lookup_option(const struct if_applet *applet, int opt) +{ + size_t iter; + for (iter = 0; applet->groups[iter] != NULL; iter++) + { + const struct if_option_group *group = applet->groups[iter]; + size_t group_iter; + + for (group_iter = 0; group_iter < group->group_size; group_iter++) + { + const struct if_option *option = &group->group[group_iter]; + + if (option->short_opt == opt) + return option; + } + } + + return NULL; +} + +static void +process_options(const struct if_applet *applet, int argc, char *argv[]) +{ + char short_opts[256] = {}, *p = short_opts; + struct option long_opts[256] = {}; + + size_t iter, long_opt_iter = 0; + for (iter = 0; applet->groups[iter] != NULL; iter++) + { + const struct if_option_group *group = applet->groups[iter]; + size_t group_iter; + + for (group_iter = 0; group_iter < group->group_size; group_iter++) + { + const struct if_option *opt = &group->group[group_iter]; + + if (opt->short_opt) + { + *p++ = opt->short_opt; + if (opt->require_argument) + *p++ = ':'; + } + + if (opt->long_opt) + { + /* XXX: handle long-opts without short-opts */ + long_opts[long_opt_iter] = (struct option) { + .name = opt->long_opt, + .has_arg = opt->require_argument ? required_argument : no_argument, + .val = opt->short_opt + }; + + long_opt_iter++; + } + } + } + + for (;;) + { + int c = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (c == -1) + break; + + const struct if_option *opt = lookup_option(applet, c); + if (opt == NULL) + break; + + opt->handle(c, opt, optarg, applet); + } +} + int main(int argc, char *argv[]) { argv0 = basename(argv[0]); - struct if_applet **app; + const struct if_applet **app; app = bsearch(argv0, applet_table, ARRAY_SIZE(applet_table), sizeof(*applet_table), @@ -62,6 +196,8 @@ main(int argc, char *argv[]) multicall_usage(EXIT_FAILURE); } + process_options(*app, argc, argv); + return (*app)->main(argc, argv); } @@ -74,6 +210,8 @@ multicall_main(int argc, char *argv[]) return main(argc - 1, argv + 1); } +struct if_applet ifupdown_applet; + void multicall_usage(int status) { @@ -97,4 +235,5 @@ struct if_applet ifupdown_applet = { .name = "ifupdown", .main = multicall_main, .usage = multicall_usage, + .groups = { &global_option_group, NULL } }; diff --git a/cmd/multicall.h b/cmd/multicall.h index 6fb26a4..d0f09c9 100644 --- a/cmd/multicall.h +++ b/cmd/multicall.h @@ -13,17 +13,40 @@ * from the use of this software. */ +#include + #ifndef IFUPDOWN_CMD_MULTICALL_H__GUARD #define IFUPDOWN_CMD_MULTICALL_H__GUARD #include "libifupdown/libifupdown.h" +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +struct if_applet; + +struct if_option { + char short_opt; + const char *long_opt; + const char *desc; + bool require_argument; + void (*const handle)(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet); +}; + +struct if_option_group { + const char *desc; + size_t group_size; + const struct if_option *group; +}; + struct if_applet { const char *name; + const char *desc; int (*const main)(int argc, char *argv[]); void (*const usage)(int status); + const struct if_option_group *groups[4]; }; extern char *argv0; +extern struct if_option_group global_option_group; #endif