From a79408231ca783ee77d1bbefd99112f9f6dbcc99 Mon Sep 17 00:00:00 2001 From: Jan 'Moskyt' Matejka Date: Thu, 25 Apr 2013 17:06:32 +0200 Subject: [PATCH] Opt: not yet compiling, written part of the parser --- ucw/opt.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++-------- ucw/opt.h | 36 +++++----- 2 files changed, 200 insertions(+), 46 deletions(-) diff --git a/ucw/opt.c b/ucw/opt.c index 0bd8df25..92c7a737 100644 --- a/ucw/opt.c +++ b/ucw/opt.c @@ -9,11 +9,21 @@ #include #include +#include #include #include #include +static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2); +static void opt_failure(const char * mesg, ...) { + va_list args; + va_start(args, mesg); + stk_vprintf(mesg, args); + exit(OPT_EXIT_BAD_ARGS); + va_end(args); +} + struct opt_section * opt_section_root; void opt_help_noexit_internal(struct opt_section * help) { @@ -79,25 +89,168 @@ void opt_help_noexit_internal(struct opt_section * help) { } void opt_init(struct opt_section * options) { - opt_section_root = options; - for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) - if (!(item->flags & OPT_VALUE_FLAGS)) { + for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) { + if (item->cls == OPT_CL_SECTION) + opt_init(item->u.section); + else 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); + ASSERT(0); } else item->flags |= opt_default_value_flags[item->cls]; } + } + opt_section_root = options; +} + +static struct opt_item * opt_find_item_longopt_section(char * str, struct opt_section * options) { + uns len = strlen(str); + struct opt_item * candidate = NULL; + + for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) { + if (item->cls == OPT_CL_SECTION) { + struct opt_item * out = opt_find_item_longopt_section(str, item->u.section); + if (out) { + if (candidate) + opt_failure("Ambiguos prefix %s: Found matching %s and %s.\n", str, candidate->name, item->name); + else + candidate = out; + } + } else if (!strncmp(item->name, str, len)) { + if (strlen(item->name) == len) + return item; + + if (candidate) + opt_failure("Ambiguos prefix %s: Found matching %s and %s.\n", str, candidate->name, item->name); + else + candidate = item; + } + } + + if (candidate) + return candidate; + else { + } +} + +static struct opt_item * opt_find_item_longopt(char * str) { + struct opt_item * out = opt_find_item_longopt_section(str, opt_section_root); + if (out == NULL) + opt_failure("Invalid argument: %s\n", str); + return out; +} + +#define OPT_NAME (longopt ? stk_printf("--%s", item->name) : stk_printf("-%c", item->letter)) +static void opt_parse_value(struct opt_item * item, char * value, int longopt) { + switch (item->cls) { + case OPT_CL_BOOL: + if (!strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true")) + *((int *) item->ptr) = 1; + else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false")) + *((int *) item->ptr) = 0; + else + opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.\n", OPT_NAME); + break; + case OPT_CL_STATIC: + { + char * e = NULL; + switch (item->type) { + case CT_INT: + e = cf_parse_int(value, item->ptr); + if (e) + opt_failure("Integer value parsing failed for argument %s: %s\n", OPT_NAME, e); + break; + case CT_U64: + e = cf_parse_u64(value, item->ptr); + if (e) + opt_failure("Unsigned 64-bit value parsing failed for argument %s: %s\n", OPT_NAME, e); + break; + case CT_DOUBLE: + e = cf_parse_double(value, item->ptr); + if (e) + opt_failure("Double value parsing failed for argument %s: %s\n", OPT_NAME, e); + break; + case CT_IP: + e = cf_parse_ip(value, item->ptr); + if (e) + opt_failure("IP parsing failed for argument %s: %s\n", OPT_NAME, e); + break; + case CT_STRING: + item->ptr = strdup(value); + break; + default: + ASSERT(0); + } + break; + } + case OPT_CL_SWITCH: + if (*((int *)item->ptr) != -1) + opt_failure("Multiple switches: %s", OPT_NAME); + else + *((int *)item->ptr) = item->u.value; + break; + case OPT_CL_INC: + if (item->flags | OPT_DECREMENT) + (*((int *)item->ptr))--; + else + (*((int *)item->ptr))++; + case OPT_CL_CALL: + + case OPT_CL_USER: + { + char * e = NULL; + e = item->u.utype->parser(value, item->ptr); + if (e) + opt_failure("User defined type value parsing failed for argument %s: %s\n", OPT_NAME, e); + break; + } + } +} +#undef OPT_NAME + +static int opt_longopt(char ** argv, int index) { + int eaten; + char * name_in = argv[index] + 2; // skipping the -- on the beginning + uns pos = strchrnul(name_in, '=') - name_in; + struct opt_item * item = opt_find_item_longopt(strndupa(name_in, pos)); + char * value = NULL; + if (item->flags | OPT_REQUIRED_VALUE) { + if (pos < strlen(name_in)) + value = name_in + pos + 1; + else { + value = argv[index+1]; + eaten++; + } + } + else if (item->flags | OPT_MAYBE_VALUE) { + if (pos < strlen(name_in)) + value = name_in + pos + 1; + } + else { + if (pos < strlen(name_in)) + opt_failure("Argument %s must not have any value.", item->name); + } } -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; +void opt_parse(char ** argv, opt_positional * callback) { + + int force_positional = 0; + for (int i=0;argv[i];i++) { + if (argv[i][0] != '-' || force_positional) { + callback(argv[i]); + } else { + if (argv[i][1] == '-') { + if (argv[i][2] == '\0') + force_positional++; + else + i += opt_longopt(argv, i); + } + else if (argv[i][1]) + i += opt_shortopt(argv, i); + else + callback(argv[i]); + } + } } #ifdef TEST @@ -105,7 +258,7 @@ int opt_parse(char ** argv, char *** posargs) { static int show_version(const char ** param UNUSED) { printf("This is a simple tea boiling console v0.1.\n"); - exit(0); + exit(EXIT_SUCCESS); } struct teapot_temperature { @@ -131,12 +284,12 @@ static enum TEAPOT_TYPE { static int english = 0; static char * name = NULL; -static uns sugar = 0; -static uns verbose = 1; +static int sugar = 0; +static int verbose = 1; static int with_gas = 0; -static uns black_magic = 0; +static int black_magic = 0; static int pray = 0; -static uns water_amount = 0; +static int water_amount = 0; static const char * teapot_temperature_parser(char * in, void * ptr) { struct teapot_temperature * temp = ptr; @@ -156,7 +309,7 @@ static const char * teapot_temperature_parser(char * in, void * ptr) { temp->scale = TEMP_REAUMUR; else { fprintf(stderr, "Unknown scale: %s\n", next); - exit(1); + exit(OPT_EXIT_BAD_ARGS); } return next + strlen(next); } @@ -167,7 +320,7 @@ static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) { } static struct cf_user_type teapot_temperature_t = { - .size = 2*sizeof(int), + .size = sizeof(struct teapot_temperature), .name = "teapot_temperature_t", .parser = (cf_parser1*) teapot_temperature_parser, .dumper = (cf_dumper1*) teapot_temperature_dumper @@ -175,7 +328,7 @@ static struct cf_user_type teapot_temperature_t = { static struct opt_section water_options = { OPT_ITEMS { - OPT_UNS('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"), + OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"), OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "Use water with gas"), OPT_END } @@ -189,22 +342,23 @@ static struct opt_section help = { 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, + OPT_HELP_OPTION, 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_UNS('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"), + OPT_INT('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, 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_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE, + "Wanted final temperature of the tea to be served\n" + "\t\tSupported scales: \tCelsius [60C], Fahrenheit [140F]," + "\t\t\tKelvin [350K], Rankine [600R] and Reaumur [50Re]" + "\t\tOnly 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"), + OPT_INT('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"), OPT_BOOL('p', "pray", pray, 0, "Pray before boiling"), OPT_HELP(""), OPT_HELP("Water options:"), @@ -223,7 +377,7 @@ int main(int argc, char ** argv) int teas_num; opt_init(&help); - teas_num = opt_parse(argv, &teas); + opt_parse(argv, NULL); for (int i=0; i #include +#define OPT_EXIT_BAD_ARGS 2 + /*** * [[opt]] ***/ @@ -36,7 +38,6 @@ 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 @@ -45,11 +46,12 @@ struct opt_item { int value; // value for OPT_SWITCH 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 + struct cf_user_type * utype; // specification of the user-defined type } u; - short flags; - enum opt_class cls; - enum cf_type type; + const char letter; // short-op + byte flags; + byte cls; // enum opt_class + byte type; // enum cf_type }; struct opt_section { @@ -62,8 +64,8 @@ struct opt_section { * Sub-items to be enclosed in OPT_ITEMS { } list * ---------------------------------------------- * - * OPT_SHOW_HELP declares --help and prints a line about that - * OPT_HELP prints a line into help() + * OPT_HELP_OPTION declares --help and prints a line about that + * OPT_HELP prints a line into help * OPT_HELP2 prints two strings onto a line using the same tab structure as the option listing * OPT_BOOL declares boolean option with an auto-negation (--sth and --no-sth); may be changed by OPT_BOOL_SET_PREFIXES * OPT_STRING, OPT_UNS, OPT_INT declare simple string/uns/int option @@ -78,13 +80,15 @@ struct opt_section { * ***/ -#define OPT_SHOW_HELP OPT_CALL(0, "help", opt_show_help_internal, OPT_NO_VALUE, "Show this help") +#define OPT_HELP_OPTION 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_HELP2(first, second) { .help = first, .cls = OPT_CL_HELP, .u.help2 = second } // FIXME: remove this #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_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 } #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_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 } +#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 } #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 } @@ -159,17 +163,13 @@ static void opt_usage(void) { void opt_init(struct opt_section * options); /** - * Parse all the arguments. - * Returns the number of positional arguments and an array of them in @posargs. + * Positional argument handler to be given to opt_parse() **/ -int opt_parse(char ** argv, char *** posargs); +typedef void opt_positional(const char * str); /** - * 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. + * Parse all the arguments. Run the @callback for each of the positional argument. **/ -int opt_get(char ** argv); +void opt_parse(char ** argv, opt_positional * callback); #endif -- 2.39.2