From: Martin Mares Date: Tue, 28 Jan 2014 17:28:57 +0000 (+0100) Subject: Opt: OPT_MULTIPLE with clists replaced by OPT_CL_MULTIPLE with gary's X-Git-Tag: v5.99~10 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=b20e17bd9d69b293adff79a9d7ed7cb199279677;p=libucw.git Opt: OPT_MULTIPLE with clists replaced by OPT_CL_MULTIPLE with gary's --- diff --git a/ucw/opt-test.c b/ucw/opt-test.c index b9bedde5..a030eefe 100644 --- a/ucw/opt-test.c +++ b/ucw/opt-test.c @@ -2,6 +2,7 @@ * UCW Library -- Parsing of command line options * * (c) 2013 Jan Moskyto Matejka + * (c) 2014 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -11,6 +12,7 @@ #include #include #include +#include static void show_version(struct opt_item * opt UNUSED, const char * value UNUSED, void * data UNUSED) { printf("This is a simple tea boiling console v0.1.\n"); @@ -45,7 +47,7 @@ static int english = 0; static int sugar = 0; static int verbose = 1; static int with_gas = 0; -static clist black_magic; +static int *black_magic; static int pray = 0; static int water_amount = 0; static char * first_tea = NULL; @@ -140,7 +142,7 @@ static struct opt_section options = { "\t\tOnly integer values allowed."), OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"), OPT_INC('q', "quiet", verbose, OPT_NEGATIVE, "\tQuiet (the more -q, the more quiet)"), - OPT_INT('b', NULL, black_magic, OPT_MULTIPLE, "\tUse black magic to make the tea extraordinarily delicious.\n\t\tMay be specified more than once to describe the amounts of black magic to be invoked in each step of tea boiling."), + OPT_INT_MULTIPLE('b', NULL, black_magic, 0, "\tUse black magic to make the tea extraordinarily delicious.\n\t\tMay be specified more than once to describe the amounts of black magic to be invoked in each step of tea boiling."), OPT_BOOL('p', "pray", pray, OPT_SINGLE, "\tPray before boiling"), OPT_STRING(OPT_POSITIONAL(1), NULL, first_tea, OPT_REQUIRED, ""), OPT_CALL(OPT_POSITIONAL_TAIL, NULL, add_tea, &tea_list, 0, ""), @@ -166,7 +168,7 @@ struct intnode { int main(int argc UNUSED, char ** argv) { cf_def_file = "etc/libucw"; - clist_init(&black_magic); + GARY_INIT(black_magic, 0); opt_parse(&options, argv+1); printf("English style: %s|", english ? "yes" : "no"); @@ -176,8 +178,9 @@ int main(int argc UNUSED, char ** argv) printf("Chosen teapot: %s|", teapot_type_str[set]); printf("Temperature: %d%s|", temperature.value, temp_scale_str[temperature.scale]); printf("Verbosity: %d|", verbose); - CLIST_FOR_EACH(struct intnode *, n, black_magic) - printf("Black magic: %d|", n->x); + uns magick = GARY_SIZE(black_magic); + for (uns i=0; i #include #include +#include #include #include @@ -20,6 +21,7 @@ static uns opt_default_value_flags[] = { [OPT_CL_BOOL] = OPT_NO_VALUE, [OPT_CL_STATIC] = OPT_MAYBE_VALUE, + [OPT_CL_MULTIPLE] = OPT_REQUIRED_VALUE, [OPT_CL_SWITCH] = OPT_NO_VALUE, [OPT_CL_INC] = OPT_NO_VALUE, [OPT_CL_CALL] = 0, @@ -110,20 +112,6 @@ static struct opt_precomputed * opt_find_item_longopt(struct opt_context * oc, c opt_failure("Invalid option --%s.", str); } -// FIXME: Use simple-lists? -#define OPT_PTR(type) ({ \ - type * ptr; \ - if (item->flags & OPT_MULTIPLE) { \ - struct { \ - cnode n; \ - type v; \ - } * n = xmalloc(sizeof(*n)); \ - clist_add_tail(item->ptr, &(n->n)); \ - ptr = &(n->v); \ - } else \ - ptr = item->ptr; \ - ptr; }) - static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * opt, char * value) { struct opt_item * item = opt->item; @@ -145,8 +133,15 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): 1/0, y/n, yes/no, true/false.", THIS_OPT); break; case OPT_CL_STATIC: + case OPT_CL_MULTIPLE: { char * e = NULL; + void * ptr; + if (item->cls == OPT_CL_STATIC) + ptr = item->ptr; + else + ptr = GARY_PUSH_GENERIC(*(void **)item->ptr); +#define OPT_PTR(type) ((type *) ptr) switch (item->type) { case CT_INT: if (!value) @@ -188,7 +183,7 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op break; case CT_USER: { - char * e = item->u.utype->parser(value, item->ptr); + char * e = item->u.utype->parser(value, ptr); if (e) opt_failure("Cannot parse the value of %s: %s", THIS_OPT, e); break; @@ -196,6 +191,7 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op default: ASSERT(0); } +#undef OPT_PTR break; } case OPT_CL_SWITCH: diff --git a/ucw/opt.h b/ucw/opt.h index aea225a2..e37fb68c 100644 --- a/ucw/opt.h +++ b/ucw/opt.h @@ -50,6 +50,8 @@ * the option is given as `--no-`'option' with no argument. * - `OPT_CL_STATIC`: options of this class just take a value and store * it in the variable. + * - `OPT_CL_MULTIPLE`: collect values from all occurrences of this + * option in a growing array (see `gary.h`). * - `OPT_CL_SWITCH`: a multiple-choice switch, which sets the variable * to a fixed value provided in option definition. * - `OPT_CL_INC`: increments the variable (or decrements, if the @@ -66,6 +68,7 @@ enum opt_class { OPT_CL_END, OPT_CL_BOOL, OPT_CL_STATIC, + OPT_CL_MULTIPLE, OPT_CL_SWITCH, OPT_CL_INC, OPT_CL_CALL, @@ -135,6 +138,7 @@ struct opt_item { * according to option class: * * - `OPT_MAYBE_VALUE` for `OPT_CL_STATIC` + * - `OPT_REQUIRED_VALUE` for `OPT_CL_MULTIPLE` * - `OPT_NO_VALUE` for `OPT_CL_BOOL`, `OPT_CL_SWITCH` and `OPT_CL_INC` * - An error is reported in all other cases. **/ @@ -158,29 +162,43 @@ struct opt_item { #define OPT_HELP_OPTION OPT_CALL(0, "help", opt_handle_help, NULL, OPT_BEFORE_CONFIG | OPT_INTERNAL | OPT_NO_VALUE, "\tShow this help") /** Boolean option. @target should be a variable of type `int`. **/ -#define OPT_BOOL(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_BOOL, .type = CT_INT } +#define OPT_BOOL(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int *), .help = desc, .flags = fl, .cls = OPT_CL_BOOL, .type = CT_INT } /** String option. @target should be a variable of type `char *`. **/ -#define OPT_STRING(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_STRING } +#define OPT_STRING(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, char **), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_STRING } -// FIXME: Check that the target is of the right type (likewise in other statically typed options) /** Ordinary integer option. @target should be a variable of type `int`. **/ -#define OPT_INT(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_INT } +#define OPT_INT(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_INT } /** 64-bit integer option. @target should be a variable of type `u64`. **/ -#define OPT_U64(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_U64 } +#define OPT_U64(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u64 *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_U64 } /** Floating-point option. @target should be a variable of type `double`. **/ -#define OPT_DOUBLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_DOUBLE } +#define OPT_DOUBLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, double *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_DOUBLE } /** IP address option, currently IPv4 only. @target should be a variable of type `u32`. **/ -#define OPT_IP(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_IP } +#define OPT_IP(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u32 *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_IP } + +/** Multi-valued string option. @target should be a growing array of `int`s. **/ +#define OPT_BOOL_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, char ***), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_STRING } + +/** Multi-valued integer option. @target should be a growing array of `int`s. **/ +#define OPT_INT_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_INT } + +/** Multi-valued 64-bit integer option. @target should be a growing array of `u64`s. **/ +#define OPT_U64_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u64 **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_U64 } + +/** Multi-valued floating-point option. @target should be a growing array of `double`s. **/ +#define OPT_DOUBLE_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, double **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_DOUBLE } + +/** Multi-valued IPv4 address option. @target should be a growing array of `u32`s. **/ +#define OPT_IP_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u32 **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_IP } /** Switch option. @target should be a variable of type `int` and it will be set to the value @val. **/ -#define OPT_SWITCH(shortopt, longopt, target, val, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_SWITCH, .type = CT_LOOKUP, .u.value = val } +#define OPT_SWITCH(shortopt, longopt, target, val, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int *), .help = desc, .flags = fl, .cls = OPT_CL_SWITCH, .type = CT_LOOKUP, .u.value = val } /** Incrementing option. @target should be a variable of type `int`. **/ -#define OPT_INC(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .flags = fl, .help = desc, .cls = OPT_CL_INC, .type = CT_INT } +#define OPT_INC(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int *), .flags = fl, .help = desc, .cls = OPT_CL_INC, .type = CT_INT } /** * When this option appears, call the function @fn with parameters @item, @value, @data, @@ -197,6 +215,9 @@ struct opt_item { **/ #define OPT_USER(shortopt, longopt, target, ttype, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .u.utype = &ttype, .flags = fl, .help = desc, .cls = OPT_CL_STATIC, .type = CT_USER } +/** Multi-valued option of user-defined type. @target should be a growing array of the right kind of items. **/ +#define OPT_USER_MULTIPLE(shortopt, longopt, target, ttype, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .u.utype = &ttype, .flags = fl, .help = desc, .cls = OPT_CL_MULTIPLE, .type = CT_USER } + /** A sub-section. **/ #define OPT_SECTION(sec) { .cls = OPT_CL_SECTION, .u.section = &sec }