From 1481eca416a467e9952dbc5e4852afe66eaf1256 Mon Sep 17 00:00:00 2001 From: Pavel Charvat Date: Thu, 4 Sep 2014 11:39:06 +0000 Subject: [PATCH] XTypes: Added support to configuration and option parser. --- ucw/conf-dump.c | 12 ++++++++++-- ucw/conf-internal.h | 3 ++- ucw/conf-intr.c | 30 ++++++++++++++++++++---------- ucw/conf-test.c | 4 ++++ ucw/conf-test.cf | 1 + ucw/conf.h | 22 ++++++++++++++++++++-- ucw/doc/conf.txt | 7 +++++-- ucw/doc/opt.txt | 5 +++-- ucw/opt.c | 8 ++++++++ ucw/opt.h | 12 ++++++++++++ 10 files changed, 85 insertions(+), 19 deletions(-) diff --git a/ucw/conf-dump.c b/ucw/conf-dump.c index cb80db70..cc5ddb37 100644 --- a/ucw/conf-dump.c +++ b/ucw/conf-dump.c @@ -3,6 +3,7 @@ * * (c) 2001--2006 Robert Spalek * (c) 2003--2012 Martin Mares + * (c) 2014 Pavel Charvat * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -14,6 +15,7 @@ #include #include #include +#include static void spaces(struct fastbuf *fb, uint nr) @@ -43,6 +45,9 @@ dump_basic(struct fastbuf *fb, void *ptr, enum cf_type type, union cf_union *u) else bprintf(fb, "??? "); break; + case CT_XTYPE: + bprintf(fb, "'%s' ", u->xtype->format(ptr, XTYPE_FMT_DEFAULT, cf_get_pool())); + break; } } @@ -55,7 +60,7 @@ dump_item(struct fastbuf *fb, struct cf_item *item, int level, void *ptr) { ptr += (uintptr_t) item->ptr; enum cf_type type = item->type; - uint size = cf_type_size(item->type, item->u.utype); + uint size = cf_type_size(item->type, &item->u); int i; spaces(fb, level); bprintf(fb, "%s: C%s #", item->name, class_names[item->cls]); @@ -64,9 +69,12 @@ dump_item(struct fastbuf *fb, struct cf_item *item, int level, void *ptr) else bprintf(fb, "%d ", item->number); if (item->cls == CC_STATIC || item->cls == CC_DYNAMIC || item->cls == CC_BITMAP) { - bprintf(fb, "T%s ", cf_type_names[type]); if (item->type == CT_USER) bprintf(fb, "U%s S%d ", item->u.utype->name, size); + else if (item->type == CT_XTYPE) + bprintf(fb, "X%s S%d ", item->u.xtype->name, size); + else + bprintf(fb, "T%s ", cf_type_names[type]); } if (item->cls == CC_STATIC) { for (i=0; inumber; i++) diff --git a/ucw/conf-internal.h b/ucw/conf-internal.h index 7ac490fb..408fc19b 100644 --- a/ucw/conf-internal.h +++ b/ucw/conf-internal.h @@ -3,6 +3,7 @@ * * (c) 2001--2006 Robert Spalek * (c) 2003--2012 Martin Mares + * (c) 2014 Pavel Charvat * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -98,7 +99,7 @@ enum cf_operation; extern char *cf_op_names[]; extern char *cf_type_names[]; -uint cf_type_size(enum cf_type type, struct cf_user_type *utype); +uint cf_type_size(enum cf_type type, const union cf_union *u); char *cf_interpret_line(struct cf_context *cc, char *name, enum cf_operation op, int number, char **pars); void cf_init_stack(struct cf_context *cc); int cf_done_stack(struct cf_context *cc); diff --git a/ucw/conf-intr.c b/ucw/conf-intr.c index cf98b35b..6ef07830 100644 --- a/ucw/conf-intr.c +++ b/ucw/conf-intr.c @@ -3,6 +3,7 @@ * * (c) 2001--2006 Robert Spalek * (c) 2003--2014 Martin Mares + * (c) 2014 Pavel Charvat * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -15,6 +16,7 @@ #include #include #include +#include #include #include @@ -45,12 +47,18 @@ static struct { }; inline uint -cf_type_size(enum cf_type type, struct cf_user_type *utype) +cf_type_size(enum cf_type type, const union cf_union *u) { - if (type < CT_USER) - return parsers[type].size; - else - return utype->size; + switch (type) + { + case CT_USER: + return u->utype->size; + case CT_XTYPE: + return u->xtype->size; + default: + ASSERT(type < ARRAY_SIZE(parsers) - 1); + return parsers[type].size; + } } static char * @@ -82,13 +90,15 @@ cf_parse_ary(uint number, char **pars, void *ptr, enum cf_type type, union cf_un for (uint i=0; iutype); + uint size = cf_type_size(type, u); if (type < CT_LOOKUP) msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * size); else if (type == CT_LOOKUP) msg = cf_parse_lookup(pars[i], ptr + i * size, u->lookup); else if (type == CT_USER) msg = u->utype->parser(pars[i], ptr + i * size); + else if (type == CT_XTYPE) + msg = (char *)u->xtype->parse(pars[i], ptr + i * size, cf_get_pool()); else ASSERT(0); if (msg) @@ -108,7 +118,7 @@ static char * interpret_set_dynamic(struct cf_item *item, int number, char **pars, void **ptr) { enum cf_type type = item->type; - uint size = cf_type_size(type, item->u.utype); + uint size = cf_type_size(type, &item->u); cf_journal_block(ptr, sizeof(void*)); // boundary checks done by the caller *ptr = gary_init(size, number, mp_get_allocator(cf_get_pool())); @@ -120,7 +130,7 @@ interpret_add_dynamic(struct cf_item *item, int number, char **pars, int *proces { enum cf_type type = item->type; void *old_p = *ptr; - uint size = cf_type_size(item->type, item->u.utype); + uint size = cf_type_size(item->type, &item->u); ASSERT(size >= sizeof(uint)); int old_nr = old_p ? GARY_SIZE(old_p) : 0; int taken = MIN(number, ABS(item->number)-old_nr); @@ -261,7 +271,7 @@ interpret_set_item(struct cf_item *item, int number, char **pars, int *processed return "Missing value"; taken = MIN(number, item->number); *processed = taken; - uint size = cf_type_size(item->type, item->u.utype); + uint size = cf_type_size(item->type, &item->u); cf_journal_block(ptr, taken * size); return cf_parse_ary(taken, pars, ptr, item->type, &item->u); case CC_DYNAMIC: @@ -337,7 +347,7 @@ cmp_items(void *i1, void *i2, struct cf_item *item) if (item->type == CT_STRING) return strcmp(* (char**) i1, * (char**) i2); else // all numeric types - return memcmp(i1, i2, cf_type_size(item->type, item->u.utype)); + return memcmp(i1, i2, cf_type_size(item->type, &item->u)); } static void * diff --git a/ucw/conf-test.c b/ucw/conf-test.c index 715d9bee..12434c98 100644 --- a/ucw/conf-test.c +++ b/ucw/conf-test.c @@ -3,6 +3,7 @@ * * (c) 2006 Robert Spalek * (c) 2012--2014 Martin Mares + * (c) 2014 Pavel Charvat */ #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include @@ -88,6 +90,7 @@ static int *look; static u16 numbers[10] = { 2, 100, 1, 5 }; static u32 bitmap1 = 0xff; static u32 bitmap2 = 3; +static intmax_t intmax; static char * parse_u16(char *string, u16 *ptr) @@ -155,6 +158,7 @@ static struct cf_section cf_top = { CF_IP("ip", &ip), CF_LOOKUP_DYN("look", &look, alphabet, 1000), CF_USER_ARY("numbers", numbers, &u16_type, 10), + CF_XTYPE("intmax", &intmax, xt_intmax), CF_BITMAP_INT("bitmap1", &bitmap1), CF_BITMAP_LOOKUP("bitmap2", &bitmap2, ((const char* const[]) { "one", "two", "three", "four", "five", "six", "seven", "eight", diff --git a/ucw/conf-test.cf b/ucw/conf-test.cf index 448ccfcb..33a96430 100644 --- a/ucw/conf-test.cf +++ b/ucw/conf-test.cf @@ -29,6 +29,7 @@ Top { bitmap1:remove 3 3 bitmap2:all bitmap2:remove eleven twelve one + intmax 1000000000 };;;;;; unknown.ignored :-) diff --git a/ucw/conf.h b/ucw/conf.h index 11026b7a..3de2938d 100644 --- a/ucw/conf.h +++ b/ucw/conf.h @@ -3,6 +3,7 @@ * * (c) 2001--2006 Robert Spalek * (c) 2003--2014 Martin Mares + * (c) 2014 Pavel Charvat * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -155,7 +156,8 @@ enum cf_type { /** Type of a single value. **/ CT_IP, // IP address CT_STRING, // string type CT_LOOKUP, // in a string table - CT_USER // user-defined type + CT_USER, // user-defined type (obsolete) + CT_XTYPE // extended type }; struct fastbuf; @@ -212,7 +214,8 @@ struct cf_item { /** Single configuration item. **/ struct cf_section *sec; // declaration of a section or a list cf_parser *par; // parser function const char * const *lookup; // NULL-terminated sequence of allowed strings for lookups - struct cf_user_type *utype; // specification of the user-defined type + struct cf_user_type *utype; // specification of the user-defined type (obsolete) + const struct xtype *xtype; // specification of the extended type } u; enum cf_class cls:16; // attribute class enum cf_type type:16; // type of a static or dynamic attribute @@ -424,6 +427,21 @@ struct cf_section { /** A section. **/ * See <> section. **/ #define CF_USER_DYN(n,p,t,c) { .cls = CC_DYNAMIC, .type = CT_USER, .name = n, .number = c, .ptr = p, .u.utype = t } +/** + * An extended type. + * See <> if you want to know more. + **/ +#define CF_XTYPE(n,p,t) { .cls = CC_STATIC, .type = CT_XTYPE, .name = n, .number = 1, .ptr = p, .u.xtype = &t } +/** + * Static array of extended types (all of the same type). + * See <>. + **/ +#define CF_XTYPE_ARY(n,p,t,c) { .cls = CC_STATIC, .type = CT_XTYPE, .name = n, .number = c, .ptr = p, .u.xtype = &t } +/** + * Dynamic array of extended types. + * See <>. + **/ +#define CF_XTYPE_DYN(n,p,t,c) { .cls = CC_DYNAMIC, .type = CT_XTYPE, .name = n, .number = c, .ptr = p, .u.xtype = &t } /** * Any number of dynamic array elements diff --git a/ucw/doc/conf.txt b/ucw/doc/conf.txt index 777dbe10..813a1835 100644 --- a/ucw/doc/conf.txt +++ b/ucw/doc/conf.txt @@ -219,8 +219,11 @@ Creating custom parsers ~~~~~~~~~~~~~~~~~~~~~~~ If you need to parse some data type the configuration system can't -handle, you can write your own parser. But before you start, you -should know a few things. +handle, you can write your own <> +and use <> macro to declare a new option. + +There is also an obsolete way to write a custom parser. +Before you start, you should know a few things. The parser needs to support <>. To accomplish that, you have to use the <> for memory allocation. diff --git a/ucw/doc/opt.txt b/ucw/doc/opt.txt index 610ce1ef..e920d64a 100644 --- a/ucw/doc/opt.txt +++ b/ucw/doc/opt.txt @@ -65,8 +65,9 @@ Most options have the following properties: - Long name: an arbitrary string. Set to NULL if the option has no long form. - Variable, where the value of the option shall be stored, together with its <>. The type is either one of the conventional - types (`int`, `uint`, etc.), or a user-defined type providing its own parser - function via <>. + types (`int`, `uint`, etc.), an extended type providing its own parser + function via <>, or an obsolete user-type + defined by <>. - <> further specifying behavior of the option (whether it is mandatory, whether it carries a value, whether it can be set repeatedly, etc.). - Help text, from which the help displayed to the user is constructed. diff --git a/ucw/opt.c b/ucw/opt.c index 713ee784..d082d03b 100644 --- a/ucw/opt.c +++ b/ucw/opt.c @@ -3,6 +3,7 @@ * * (c) 2013 Jan Moskyto Matejka * (c) 2014 Martin Mares + * (c) 2014 Pavel Charvat * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -201,6 +202,13 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op opt_failure("Cannot parse the value of %s: %s", THIS_OPT, e); break; } + case CT_XTYPE: + { + const char * e = item->u.xtype->parse(value, ptr, cf_get_pool()); + if (e) + opt_failure("Cannot parse the value of %s: %s", THIS_OPT, e); + break; + } default: ASSERT(0); } diff --git a/ucw/opt.h b/ucw/opt.h index 20df7fd7..c8c683c0 100644 --- a/ucw/opt.h +++ b/ucw/opt.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -110,6 +111,7 @@ struct opt_item { void (* call)(const struct opt_item * opt, const char * value, void * data); // function to call for OPT_CL_CALL void (* hook)(const struct opt_item * opt, uint event, const char * value, void * data); // function to call for OPT_CL_HOOK struct cf_user_type * utype; // specification of the user-defined type for CT_USER + const struct xtype * xtype; // specification of the extended type for CT_XTYPE } u; u16 flags; // as defined below (for hooks, event mask is stored instead) byte cls; // enum opt_class @@ -239,6 +241,16 @@ struct opt_item { /** 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 } +/** + * An option with user-defined syntax. @xtype is a <> + * describing the syntax, @target is a variable of the corresponding type. If the @OPT_REQUIRED_VALUE + * flag is not set, the parser must be able to parse a NULL value. + **/ +#define OPT_XTYPE(shortopt, longopt, target, ttype, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .u.xtype = &ttype, .flags = fl, .help = desc, .cls = OPT_CL_STATIC, .type = CT_XTYPE } + +/** Multi-valued option of extended type. @target should be a growing array of the right kind of items. **/ +#define OPT_XTYPE_MULTIPLE(shortopt, longopt, target, ttype, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .u.xtype = &ttype, .flags = fl, .help = desc, .cls = OPT_CL_MULTIPLE, .type = CT_XTYPE } + /** A sub-section. **/ #define OPT_SECTION(sec) { .cls = OPT_CL_SECTION, .u.section = &sec } -- 2.39.2