From: Jan 'Moskyt' Matejka Date: Mon, 22 Apr 2013 10:57:11 +0000 (+0200) Subject: Opt: interface ready for review X-Git-Tag: v5.99~25^2~58 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=a9a0d2d5186ab14bc9cf91dacb75d5d3bb99762e;p=libucw.git Opt: interface ready for review --- diff --git a/ucw/opt.c b/ucw/opt.c index 7989ab84..0bd8df25 100644 --- a/ucw/opt.c +++ b/ucw/opt.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -17,8 +18,9 @@ struct opt_section * opt_section_root; void opt_help_noexit_internal(struct opt_section * help) { uns first_column = 0; + for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) { - if (item->flags | OPT_NO_HELP) continue; + if (item->flags & OPT_NO_HELP) continue; if (item->cls == OPT_CL_HELP && item->u.help2 == NULL) continue; if (item->cls == OPT_CL_SECTION) continue; @@ -35,9 +37,11 @@ void opt_help_noexit_internal(struct opt_section * help) { linelen = strlen("--") + strlen(item->name); } - if (item->flags | OPT_REQUIRED_VALUE) { + ASSERT(item->flags & OPT_VALUE_FLAGS); + + if (item->flags & OPT_REQUIRED_VALUE) { linelen += strlen("=value"); - } else if (!(item->flags | OPT_NO_VALUE)) { + } else if (item->flags & OPT_MAYBE_VALUE) { linelen += strlen("(=value)"); } @@ -52,9 +56,9 @@ void opt_help_noexit_internal(struct opt_section * help) { spaces[first_column] = 0; -#define VAL(it) ((it->flags | OPT_REQUIRED_VALUE) ? "=value" : ((it->flags | OPT_NO_VALUE) ? "" : "(=value)")) +#define VAL(it) ((it->flags & OPT_REQUIRED_VALUE) ? "=value" : ((it->flags & OPT_NO_VALUE) ? "" : "(=value)")) for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) { - if (item->flags | OPT_NO_HELP) continue; + if (item->flags & OPT_NO_HELP) continue; if (item->cls == OPT_CL_HELP) { fprintf(stderr, "%s", item->help); @@ -74,12 +78,48 @@ void opt_help_noexit_internal(struct opt_section * help) { } } -void opt_parse(struct opt_section * options) { +void opt_init(struct opt_section * options) { opt_section_root = options; - opt_help(); + for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) + if (!(item->flags & OPT_VALUE_FLAGS)) { + if (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER) { + fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name); + exit(2); + } else + item->flags |= opt_default_value_flags[item->cls]; + } +} + +int opt_parse(char ** argv, char *** posargs) { + // Temporary. To be fixed. + static char ** d; + d = xmalloc(sizeof (*d) * 2); + d[0] = "darjeeling"; + d[1] = "puerh"; + *posargs = d; + return 2; } #ifdef TEST +#include + +static int show_version(const char ** param UNUSED) { + printf("This is a simple tea boiling console v0.1.\n"); + exit(0); +} + +struct teapot_temperature { + enum { + TEMP_CELSIUS = 0, + TEMP_FAHRENHEIT, + TEMP_KELVIN, + TEMP_REAUMUR, + TEMP_RANKINE + } scale; + int value; +} temperature; + +static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" }; static enum TEAPOT_TYPE { TEAPOT_STANDARD = 0, @@ -98,20 +138,41 @@ static uns black_magic = 0; static int pray = 0; static uns water_amount = 0; -static struct teapot_temperature { - enum { - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TEMP_KELVIN, - TEMP_REAUMUR - } scale; - int value; -} temperature; +static const char * teapot_temperature_parser(char * in, void * ptr) { + struct teapot_temperature * temp = ptr; + const char * next; + const char * err = str_to_int(&temp->value, in, &next, 0); + if (err) + return err; + if (!strcmp("C", next)) + temp->scale = TEMP_CELSIUS; + else if (!strcmp("F", next)) + temp->scale = TEMP_FAHRENHEIT; + else if (!strcmp("K", next)) + temp->scale = TEMP_KELVIN; + else if (!strcmp("R", next)) + temp->scale = TEMP_RANKINE; + else if (!strcmp("Re", next)) + temp->scale = TEMP_REAUMUR; + else { + fprintf(stderr, "Unknown scale: %s\n", next); + exit(1); + } + return next + strlen(next); +} -static int parse_temperature(const char * param, void * target) { - return 1; +static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) { + struct teapot_temperature * temp = ptr; + bprintf(fb, "%d%s", temp->value, temp_scale_str[temp->scale]); } +static struct cf_user_type teapot_temperature_t = { + .size = 2*sizeof(int), + .name = "teapot_temperature_t", + .parser = (cf_parser1*) teapot_temperature_parser, + .dumper = (cf_dumper1*) teapot_temperature_dumper +}; + static struct opt_section water_options = { OPT_ITEMS { OPT_UNS('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"), @@ -123,18 +184,24 @@ static struct opt_section water_options = { static struct opt_section help = { OPT_ITEMS { OPT_HELP("A simple tea boiling console."), + OPT_HELP("Usage: teapot [options] name-of-the-tea"), + OPT_HELP("Black, green or white tea supported as well as fruit or herbal tea."), + OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."), + OPT_HELP(""), OPT_HELP("Options:"), - OPT_SHOW_HELP(0), + OPT_SHOW_HELP, + OPT_CALL('V', "version", show_version, OPT_NO_VALUE, "Show the version"), + OPT_HELP(""), OPT_BOOL('e', "english-style", english, 0, "English style (with milk)"), - OPT_STRING('n', "name", name, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Name of the tea to be prepared"), OPT_UNS('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"), OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "Standard teapot"), OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "Exclusive teapot"), OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "Transparent glass teapot"), OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "Use user's hands as a teapot (a bit dangerous)"), - OPT_USER('t', "temperature", temperature, parse_temperature, OPT_REQUIRED_VALUE, "Wanted final temperature of the tea to be served"), + OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE, "Wanted final temperature of the tea to be served"), OPT_HELP2("", "Supported scales: Celsius [60C], Fahrenheit [140F],"), OPT_HELP2("", " Kelvin [350K], Rankine [600R] and Reaumur [50Re]"), + OPT_HELP2("", "Only integer values allowed."), OPT_INC('v', "verbose", verbose, 0, "Verbose (the more -v, the more verbose)"), OPT_INC('q', "quiet", verbose, OPT_DECREMENT, "Quiet (the more -q, the more quiet)"), OPT_UNS('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"), @@ -146,9 +213,22 @@ static struct opt_section help = { } }; +static void boil_tea(const char * name) { + printf("Boiling a tea: %s\n", name); +} + int main(int argc, char ** argv) { - opt_parse(&help); + char ** teas; + int teas_num; + + opt_init(&help); + teas_num = opt_parse(argv, &teas); + + for (int i=0; i +#include + #include #include @@ -25,39 +28,34 @@ enum opt_class { OPT_CL_STATIC, // static value OPT_CL_SWITCH, // lookup/switch OPT_CL_INC, // incremental value + OPT_CL_CALL, // call a function OPT_CL_USER, // user defined value OPT_CL_SECTION, // subsection OPT_CL_HELP, // help line }; -enum opt_type { - OPT_CT_INT, OPT_CT_64, OPT_CT_DOUBLE, // number - OPT_CT_STRING, // string - OPT_CT_LOOKUP, // lookup/switch - OPT_CT_USER, // user defined -}; - -typedef int opt_custom_parser(const char * param, void * target); +typedef void opt_custom_function(const char ** param); struct opt_section; struct opt_item { const char letter; // short-op - const char *name; // long-op - void *ptr; // where to save - const char *help; // description in --help + const char * name; // long-op + void * ptr; // where to save + const char * help; // description in --help union opt_union { - struct opt_section *section; // subsection for OPT_SECTION + struct opt_section * section; // subsection for OPT_SECTION int value; // value for OPT_SWITCH - opt_custom_parser *parser; // parser for OPT_USER - const char *help2; // second value for OPT_HELP2 + const char * help2; // second value for OPT_HELP2 + int (* call)(const char ** param); // function to call for OPT_CALL + struct cf_user_type * utype; // specification of the user-defined type } u; short flags; enum opt_class cls; - enum opt_type type; + enum cf_type type; }; struct opt_section { - struct opt_item *opt; + struct opt_item * opt; }; #define OPT_ITEMS .opt = ( struct opt_item[] ) /** List of sub-items. **/ @@ -72,6 +70,7 @@ struct opt_section { * OPT_SWITCH declares one choice of a switch statement; these have common target and different `value`s; last wins unless OPT_SINGLE is set; * parser fails if it matches an OPT_SWITCH with OPT_SINGLE set and also target set. * Target must be of signed integer type; it is set to -1 if no switch appears at the command-line. + * OPT_CALL calls the given function with all the remaining command line, it returns the number of arguments to be skipped. * OPT_USER declares a custom type of value; parser is of type opt_custom_parser * and returns 1 on success and 0 on failure * OPT_INC declares an incremental value like -v/--verbose @@ -79,16 +78,17 @@ struct opt_section { * * **/ -#define OPT_SHOW_HELP(flags) OPT_USER(0, "help", *(NULL), opt_help_success2, flags, "Show this help") +#define OPT_SHOW_HELP OPT_CALL(0, "help", opt_show_help_internal, OPT_NO_VALUE, "Show this help") #define OPT_HELP(line) OPT_HELP2(line, NULL) #define OPT_HELP2(first, second) { .help = first, .cls = OPT_CL_HELP, .u.help2 = second } -#define OPT_BOOL(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_BOOL, .type = OPT_CT_INT } -#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 = OPT_CT_STRING } -#define OPT_UNS(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, uns*), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = OPT_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 = OPT_CT_INT } -#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 = OPT_CT_INT, .u.value = val } -#define OPT_USER(shortopt, longopt, target, pa, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .u.parser = pa, .flags = fl, .help = desc, .cls = OPT_CL_USER, .type = OPT_CT_USER } -#define OPT_INC(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .flags = fl, .help = desc, .cls = OPT_CL_INC, .type = OPT_CT_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_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 } +#define OPT_UNS(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, uns *), .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 } +#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 } +#define OPT_CALL(shortopt, longopt, fn, fl, desc) { .letter = shortopt, .name = longopt, .ptr = NULL, .help = desc, .u.call = fn, .flags = fl, .cls = OPT_CL_CALL, .type = CT_USER } +#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_USER, .type = CT_USER } +#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_SECTION(sec) { .cls = OPT_CL_SECTION, .u.section = &sec } #define OPT_END { .cls = OPT_CL_END } @@ -96,9 +96,28 @@ struct opt_section { #define OPT_REQUIRED 0x1 // Argument must appear at the command line #define OPT_REQUIRED_VALUE 0x2 // Argument must have a value #define OPT_NO_VALUE 0x4 // Argument must have no value -#define OPT_DECREMENT 0x8 // Reversing the effect of OPT_INC -#define OPT_SINGLE 0x10 // Argument must appear at most once -#define OPT_NO_HELP 0x20 // Omit this line from help +#define OPT_MAYBE_VALUE 0x8 // Argument may have a value +#define OPT_VALUE_FLAGS (OPT_REQUIRED_VALUE | OPT_NO_VALUE | OPT_MAYBE_VALUE) +#define OPT_DECREMENT 0x10 // Reversing the effect of OPT_INC +#define OPT_SINGLE 0x20 // Argument must appear at most once +#define OPT_NO_HELP 0x40 // Omit this line from help + +/** Value flags defaults: + * OPT_NO_VALUE for OPT_BOOL, OPT_SWITCH and OPT_INC + * OPT_MAYBE_VALUE for OPT_STRING, OPT_UNS, OPT_INT + * Some of the value flags (OPT_NO_VALUE, OPT_MAYBE_VALUE, OPT_REQUIRED_VALUE) + * must be specified for OPT_CALL and OPT_USER. + */ +static uns opt_default_value_flags[] = { + [OPT_CL_BOOL] = OPT_NO_VALUE, + [OPT_CL_STATIC] = OPT_MAYBE_VALUE, + [OPT_CL_SWITCH] = OPT_NO_VALUE, + [OPT_CL_INC] = OPT_NO_VALUE, + [OPT_CL_CALL] = 0, + [OPT_CL_USER] = 0, + [OPT_CL_SECTION] = 0, + [OPT_CL_HELP] = 0 +}; extern struct opt_section * opt_section_root; void opt_help_noexit_internal(struct opt_section * help); @@ -111,7 +130,7 @@ static void opt_usage_noexit(void) { fprintf(stderr, "Run with argument --help for more information.\n"); } -static int opt_help_success2(const char * param UNUSED, void * target UNUSED) { +static int opt_show_help_internal(const char ** param UNUSED) { opt_help_noexit(); exit(0); } @@ -126,6 +145,23 @@ static void opt_usage(void) { exit(1); } -void opt_parse(struct opt_section * options); +/** + * Init the opt engine. + */ +void opt_init(struct opt_section * options); + +/** + * Parse all the arguments. + * Returns the number of positional arguments and an array of them in @posargs. + */ +int opt_parse(char ** argv, char *** posargs); + +/** + * Parse all the arguments until first positional argument is found. + * Returns the position of that argument in argv. + * On next call of this function, start from the next item in argv. + * Iterate this function to get all the positional arguments. + */ +int opt_get(char ** argv); #endif