From 9f3c9f939f6f816730ad47d46f4d534b046cbe35 Mon Sep 17 00:00:00 2001 From: Robert Spalek Date: Mon, 17 Apr 2006 20:15:39 +0200 Subject: [PATCH] designed the interface of the new configuration reader. no code has been written yet, because I better wait for an opinion first. a tester app that currently just checks compilability of all messy cf-declaration macros is included. don't worry about filenames; they will be eventually renamed. --- lib/Makefile | 4 ++ lib/conf2-test.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++ lib/conf2.h | 72 ++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 lib/conf2-test.c create mode 100644 lib/conf2.h diff --git a/lib/Makefile b/lib/Makefile index 35b865a1..757d2149 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,6 +6,9 @@ ifdef CONFIG_UCW_DBTOOL PROGS+=$(o)/lib/db-tool endif +# hey dude, I'm Robert! +PROGS+=$(o)/lib/conf2-test + LIBUCW_MODS= \ alloc alloc_str realloc mempool mempool-str mempool-fmt \ mmap pagecache partmap hashfunc \ @@ -67,6 +70,7 @@ $(o)/lib/lizard.o: CFLAGS += $(COPT2) -funroll-loops $(o)/lib/db-test: $(o)/lib/db-test.o $(LIBUCW) $(o)/lib/db-tool: $(o)/lib/db-tool.o $(LIBUCW) $(o)/lib/conf-test: $(o)/lib/conf-test.o $(LIBUCW) +$(o)/lib/conf2-test: $(o)/lib/conf2-test.o $(LIBUCW) $(o)/lib/sort-test: $(o)/lib/sort-test.o $(LIBUCW) $(o)/lib/lfs-test: $(o)/lib/lfs-test.o $(LIBUCW) $(o)/lib/hash-test: $(o)/lib/hash-test.o $(LIBUCW) diff --git a/lib/conf2-test.c b/lib/conf2-test.c new file mode 100644 index 00000000..540ffed3 --- /dev/null +++ b/lib/conf2-test.c @@ -0,0 +1,106 @@ +/* + * Insane tester of reading configuration files + * + * (c) 2006 Robert Spalek + */ + +#include "lib/lib.h" +#include "lib/conf2.h" +#include "lib/clists.h" + +#include +#include + +struct sub_sect_1 { + byte *name; + byte *level; + int confidence; +}; + +static byte * +init_sec_1(void *ptr, struct cf_section *sec UNUSED) +{ + struct sub_sect_1 *s = ptr; + s->name = "unknown"; + s->level = "default"; + s->confidence = 5; + return NULL; +} + +static byte * +commit_sec_1(void *ptr, struct cf_section *sec UNUSED) +{ + struct sub_sect_1 *s = ptr; + if (s->confidence < 0 || s->confidence > 10) + return "Well, this can't be"; + return NULL; +} + +static struct cf_section cf_sec_1 = { + .size = sizeof(struct sub_sect_1), + .init = init_sec_1, + .commit = commit_sec_1, + .cfg = (struct cf_item[]) { +#define F(x) CF_FIELD(struct sub_sect_1, x) + CF_STRING("name", F(name)), + CF_STRING("level", F(level)), + CF_INT("confidence", F(confidence)), + CF_END +#undef F + } +}; + +static int nr1 = 15; +static int *nrs1 = DEFAULT_ARRAY(int, 5, 5, 4, 3, 2, 1); +static int *nrs2; +static byte *str1 = "no worries"; +static byte **str2 = DEFAULT_ARRAY(byte *, 2, "Alice", "Bob"); +static u64 u1 = 0xCafeBeefDeadC00ll; +static double d1 = -1.1; +static struct sub_sect_1 sec_1 = { "Charlie", "WBAFC", 0 }; +static struct cnode secs; +static time_t t1, t2; + +static byte * +commit_top(void *ptr UNUSED, struct cf_section *sec UNUSED) +{ + return NULL; +} + +static byte * +time_parser(uns nr_pars, byte **pars, void *sec_ptr, struct cf_section *sec, uns index) +{ + if (nr_pars != 0 && nr_pars != 1) + return "Either now or 1 parameter!"; + ASSERT(!sec_ptr); + time_t t = nr_pars ? atoi(pars[0]) : time(NULL); + if (sec->cfg[index].name[0] == 'F') + t1 = t; + else + t2 = t; + return NULL; +} + +static struct cf_section cf_top = { + .commit = commit_top, + .cfg = (struct cf_item []) { + CF_INT("nr1", &nr1), + CF_INT_AR("nrs1", &nrs1, 5), + CF_INT_AR("nrs2", &nrs2, -1000), + CF_STRING("str1", &str1), + CF_STRING_AR("str2", &str2, 2), + CF_U64("u1", &u1), + CF_DOUBLE("d1", &d1), + CF_FUNCTION("FirstTime", time_parser), + CF_FUNCTION("SecondTime", time_parser), + CF_SUB_SECTION("master", &sec_1, &cf_sec_1), + CF_LINK_LIST("slaves", &secs, &cf_sec_1), + CF_END + } +}; + +int +main(void) +{ + return 0; +} diff --git a/lib/conf2.h b/lib/conf2.h new file mode 100644 index 00000000..dfb024d3 --- /dev/null +++ b/lib/conf2.h @@ -0,0 +1,72 @@ +/* + * UCW Library -- Reading of configuration files + * + * (c) 2006 Robert Spalek + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#ifndef _LIB_CONF2_H +#define _LIB_CONF2_H + +enum cf_type { + CT_END, // end of list + CT_INT, CT_U64, CT_DOUBLE, // number types + CT_STRING, // string type + CT_FUNCTION, // arbitrary parser function + CT_SUB_SECTION, // sub-section appears exactly once + CT_LINK_LIST // link-list with 0..many nodes +}; + +struct cf_section; +typedef byte *cf_hook(void *sec_ptr, struct cf_section *sec); + /* An init- or commit-hook gets a pointer to the sub-section or NULL if this + * is the global section, and a declaration of the section it is called on. + * It returns an error message or NULL if everything is all right. */ +typedef byte *cf_parser(uns nr_pars, byte **pars, void *sec_ptr, struct cf_section *sec, uns index); + /* A parser function gets an array of strings and stores it in any way it + * likes into its own data structures. It gets a pointer to the sub-section + * or NULL if this is the global section, a declaration of the section it is + * called in, and the index of the item it is called on. It returns an error + * message or NULL if everything is all right. */ + +struct cf_item { + enum cf_type type; + byte *name; + int number; // number of values: k>0 means exactly k, k<0 means at most k + void *ptr; // pointer to a global variable or an offset in a sub-section + struct cf_section *sub; // declaration of a sub-section or link-list +}; + +struct cf_section { + uns size; // 0 for a global block, sizeof(struct) for a sub-section + cf_hook *init; // fills in default values + cf_hook *commit; // verifies parsed data and checks ranges (optional) + struct cf_item *cfg; // CT_END-terminated array of items +}; + +#define CHECK_VAR_TYPE(x,type) ((x)-(type)0 + (type)0) + // for a pointer x it returns x, and performs a compile-time check whether typeof(x)==type +#define DEFAULT_ARRAY(type,len,val...) (type[]) { (type)len, ##val } + 1 + // creates an array with an allocated space in the front for the (Pascal-like) length +#define CF_FIELD(str,f) &((str*)0)->f + // returns a pointer to a field inside a structure suitable for passing as cf_item->ptr + +#define CF_END { .type = CT_END } + // please better put this at the end of each section +#define CF_INT(n,p) { .type = CT_INT, .name = n, .number = 1, .ptr = CHECK_VAR_TYPE(p,int*) } +#define CF_U64(n,p) { .type = CT_U64, .name = n, .number = 1, .ptr = CHECK_VAR_TYPE(p,u64*) } +#define CF_DOUBLE(n,p) { .type = CT_DOUBLE, .name = n, .number = 1, .ptr = CHECK_VAR_TYPE(p,double*) } +#define CF_STRING(n,p) { .type = CT_STRING, .name = n, .number = 1, .ptr = CHECK_VAR_TYPE(p,byte**) } +#define CF_FUNCTION(n,p) { .type = CT_FUNCTION, .name = n, .number = 1, .ptr = CHECK_VAR_TYPE(p,cf_parser*) } +#define CF_SUB_SECTION(n,p,s) { .type = CT_SUB_SECTION, .name = n, .number = 1, .ptr = p, .sub = s } +#define CF_LINK_LIST(n,p,s) { .type = CT_LINK_LIST, .name = n, .number = 1, .ptr = CHECK_VAR_TYPE(p,struct cnode*), .sub = s } + // use the macros above to declare configuration items for single variables +#define CF_INT_AR(n,p,c) { .type = CT_INT, .name = n, .number = c, .ptr = CHECK_VAR_TYPE(p,int**) } +#define CF_U64_AR(n,p,c) { .type = CT_U64, .name = n, .number = c, .ptr = CHECK_VAR_TYPE(p,u64**) } +#define CF_DOUBLE_AR(n,p,c) { .type = CT_DOUBLE, .name = n, .number = c, .ptr = CHECK_VAR_TYPE(p,double**) } +#define CF_STRING_AR(n,p,c) { .type = CT_STRING, .name = n, .number = c, .ptr = CHECK_VAR_TYPE(p,byte***) } + // use the macros above to declare configuration items for arrays of variables + +#endif -- 2.39.2