struct sub_sect_1 {
byte *name;
byte *level;
- int confidence;
+ int confidence[2];
+ double *list;
};
static byte *
{
s->name = "unknown";
s->level = "default";
- s->confidence = 5;
+ s->confidence[0] = 5;
+ s->confidence[1] = 6;
return NULL;
}
static byte *
commit_sec_1(struct sub_sect_1 *s)
{
- if (s->confidence < 0 || s->confidence > 10)
+ if (s->confidence[0] < 0 || s->confidence[0] > 10)
return "Well, this can't be";
return NULL;
}
CF_INIT(init_sec_1)
CF_COMMIT(commit_sec_1)
#define F(x) PTR_TO(struct sub_sect_1, x)
- CF_ITEMS(
+ CF_START_ITEMS
CF_STRING("name", F(name))
CF_STRING("level", F(level))
- CF_INT("confidence", F(confidence))
- )
+ CF_INT_ARY("confidence", F(confidence[0]), 2) // XXX: the [0] is needed for the sake of macros :-(
+ CF_DOUBLE_DYN("list", F(list), 100)
+ CF_END_ITEMS
#undef F
};
static int nr1 = 15;
-static int *nrs1 = ARRAY_ALLOC(int, 5, 5, 4, 3, 2, 1);
-static int *nrs2;
+static int *nrs1 = DYN_ALLOC(int, 5, 5, 4, 3, 2, 1);
+static int nrs2[5];
static byte *str1 = "no worries";
-static byte **str2 = ARRAY_ALLOC(byte *, 2, "Alice", "Bob");
+static byte **str2 = DYN_ALLOC(byte *, 2, "Alice", "Bob");
static u64 u1 = 0xCafeBeefDeadC00ll;
static double d1 = -1.1;
-static struct sub_sect_1 sec1 = { "Charlie", "WBAFC", 0 };
+static struct sub_sect_1 sec1 = { "Charlie", "WBAFC", { 0, -1} };
static struct clist secs;
static time_t t1, t2;
return NULL;
}
-static struct cf_section cf_top = {
+static struct cf_section cf_top UNUSED = {
CF_COMMIT(commit_top)
- CF_ITEMS(
+ CF_START_ITEMS
CF_INT("nr1", &nr1)
- CF_INT_ARY("nrs1", &nrs1, 5)
- CF_INT_ARY("nrs2", &nrs2, -1000)
+ CF_INT_DYN("nrs1", &nrs1, 1000)
+ CF_INT_ARY("nrs2", nrs2, 5)
CF_STRING("str1", &str1)
- CF_STRING_ARY("str2", &str2, 2)
+ CF_STRING_DYN("str2", &str2, 2)
CF_U64("u1", &u1)
CF_DOUBLE("d1", &d1)
CF_PARSER("FirstTime", &t1, time_parser, -1)
CF_PARSER("SecondTime", &t2, time_parser, 1)
CF_SECTION("master", &sec1, &cf_sec_1)
CF_LIST("slaves", &secs, &cf_sec_1)
- )
+ CF_END_ITEMS
};
int
/* Undo journal */
-static uns journal_active;
-
-uns
-cf_journal_active(uns flag)
-{
- uns f = journal_active;
- journal_active = flag;
- return f;
-}
+static uns journal_active; // controls whether a call to cf_journal_block() is taken into account
void
cf_journal_block(void *ptr UNUSED, uns len UNUSED)
{
+ if (!journal_active)
+ return;
}
/* Parsers for standard types */
static char cf_rngerr[] = "Number out of range";
static byte *
-cf_parse_int(uns number, byte **pars, int *ptr)
+parse_int(uns number, byte **pars, int *ptr)
{
cf_journal_block(ptr, number * sizeof(int));
for (uns i=0; i<number; i++)
}
static byte *
-cf_parse_u64(uns number, byte **pars, u64 *ptr)
+parse_u64(uns number, byte **pars, u64 *ptr)
{
cf_journal_block(ptr, number * sizeof(u64));
for (uns i=0; i<number; i++)
}
static byte *
-cf_parse_double(uns number, byte **pars, double *ptr)
+parse_double(uns number, byte **pars, double *ptr)
{
cf_journal_block(ptr, number * sizeof(double));
for (uns i=0; i<number; i++)
}
static byte *
-cf_parse_string(uns number, byte **pars, byte **ptr)
+parse_string(uns number, byte **pars, byte **ptr)
{
cf_journal_block(ptr, number * sizeof(byte*));
for (uns i=0; i<number; i++)
return NULL;
}
-static byte *
-cf_parse_int_ary(uns number, byte **pars, int **ptr)
-{
- cf_journal_block(ptr, sizeof(int**));
- *ptr = (int*) cf_malloc((number+1) * sizeof(int*)) + 1;
- ARRAY_LEN(*ptr) = number;
- uns old_flag = cf_journal_active(0);
- byte *msg = cf_parse_int(number, pars, *ptr);
- cf_journal_active(old_flag);
- return msg;
-}
-
-static byte *
-cf_parse_u64_ary(uns number, byte **pars, u64 **ptr)
-{
- cf_journal_block(ptr, sizeof(u64**));
- *ptr = (u64*) cf_malloc((number+1) * sizeof(u64*)) + 1;
- ARRAY_LEN(*ptr) = number;
- uns old_flag = cf_journal_active(0);
- byte *msg = cf_parse_u64(number, pars, *ptr);
- cf_journal_active(old_flag);
- return msg;
-}
-
-static byte *
-cf_parse_double_ary(uns number, byte **pars, double **ptr)
-{
- cf_journal_block(ptr, sizeof(double**));
- *ptr = (double*) cf_malloc((number+1) * sizeof(double*)) + 1;
- ARRAY_LEN(*ptr) = number;
- uns old_flag = cf_journal_active(0);
- byte *msg = cf_parse_double(number, pars, *ptr);
- cf_journal_active(old_flag);
- return msg;
-}
+/* Register size of and parser for each basic type */
+static struct {
+ uns size;
+ void *parser;
+} parsers[] = {
+ { sizeof(int), parse_int },
+ { sizeof(u64), parse_u64 },
+ { sizeof(double), parse_double },
+ { sizeof(byte*), parse_string }
+};
static byte *
-cf_parse_string_ary(uns number, byte **pars, byte ***ptr)
+cf_parse_dyn(uns number, byte **pars, void **ptr, enum cf_type type)
{
- cf_journal_block(ptr, sizeof(byte***));
- *ptr = (byte**) cf_malloc((number+1) * sizeof(byte**)) + 1;
- ARRAY_LEN(*ptr) = number;
- uns old_flag = cf_journal_active(0);
- byte *msg = cf_parse_string(number, pars, *ptr);
- cf_journal_active(old_flag);
+ cf_journal_block(ptr, sizeof(void*));
+ *ptr = cf_malloc((number+1) * parsers[type].size) + parsers[type].size;
+ * (uns*) (ptr - parsers[type].size) = number;
+ uns old_flag = journal_active;
+ journal_active = 0;
+ byte *msg = ((cf_parser*) parsers[type].parser) (number, pars, *ptr);
+ journal_active = old_flag;
return msg;
}
#ifndef _UCW_CONF2_H
#define _UCW_CONF2_H
+enum cf_class {
+ CC_END, // end of list
+ CC_STATIC, // single variable or static array
+ CC_DYNAMIC, // dynamically allocated array
+ CC_PARSER, // arbitrary parser function
+ CC_SECTION, // section appears exactly once
+ CC_LIST // list with 0..many nodes
+};
+
enum cf_type {
- CT_END, // end of list
CT_INT, CT_U64, CT_DOUBLE, // number types
- CT_STRING, // string type
- CT_PARSER, // arbitrary parser function
- CT_SECTION, // section appears exactly once
- CT_LIST // list with 0..many nodes
+ CT_STRING // string type
};
+typedef byte *cf_parser(uns number, byte **pars, void *ptr);
+ /* A parser function gets an array of (strdup'ed) strings and a pointer with
+ * the customized information (most likely the target address). It can store
+ * the parsed value anywhere in any way it likes, however it must first call
+ * cf_journal_block() on the overwritten memory block. It returns an error
+ * message or NULL if everything is all right. */
typedef byte *cf_hook(void *ptr);
/* An init- or commit-hook gets a pointer to the section or NULL if this
* is the global section. It returns an error message or NULL if everything
- * is all right. */
-typedef byte *cf_parser(uns number, byte **pars, void *ptr);
- /* A parser function an array of strings and stores the parsed value in any
- * way it likes into *ptr. It returns an error message or NULL if everything
- * is all right. */
+ * is all right. The init-hook should fill in default values (needed for
+ * dynamically allocated nodes of link lists or for filling global variables
+ * that are run-time dependent). The commit-hook should perform sanity
+ * checks and postprocess the parsed values. Commit-hooks must call
+ * cf_journal_block() too. */
struct cf_section;
struct cf_item {
- enum cf_type type;
+ enum cf_class cls;
byte *name;
- int number; // number of values: k>=0 means exactly k, k<0 means at most -k
+ int number; // length of an array or #parameters of a parser
void *ptr; // pointer to a global variable or an offset in a section
union {
+ enum cf_type type; // type of a static or dynamic attribute
struct cf_section *sub; // declaration of a section or a list
cf_parser *par; // parser function
- } ptr2;
+ } u;
};
struct cf_section {
uns size; // 0 for a global block, sizeof(struct) for a section
- cf_hook *init; // fills in default values
- cf_hook *commit; // verifies parsed data and checks ranges (optional)
+ cf_hook *init; // fills in default values (otherwise 0's are used)
+ cf_hook *commit; // verifies parsed data (optional)
struct cf_item *cfg; // CT_END-terminated array of items
};
#define CF_TYPE(s) .size = sizeof(s),
#define CF_INIT(f) .init = (cf_hook*) f,
#define CF_COMMIT(f) .commit = (cf_hook*) f,
-#define CF_ITEMS(i) .cfg = ( struct cf_item[] ) { i { .type = CT_END } },
-/* Configuration items for single variables */
-#define CF_INT(n,p) { .type = CT_INT, .name = n, .number = 1, .ptr = CHECK_PTR_TYPE(p,int*) },
-#define CF_U64(n,p) { .type = CT_U64, .name = n, .number = 1, .ptr = CHECK_PTR_TYPE(p,u64*) },
-#define CF_DOUBLE(n,p) { .type = CT_DOUBLE, .name = n, .number = 1, .ptr = CHECK_PTR_TYPE(p,double*) },
-#define CF_STRING(n,p) { .type = CT_STRING, .name = n, .number = 1, .ptr = CHECK_PTR_TYPE(p,byte**) },
-/* Configuration items for arrays of variables */
-#define CF_INT_ARY(n,p,c) { .type = CT_INT, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,int**) },
-#define CF_U64_ARY(n,p,c) { .type = CT_U64, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,u64**) },
-#define CF_DOUBLE_ARY(n,p,c) { .type = CT_DOUBLE, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,double**) },
-#define CF_STRING_ARY(n,p,c) { .type = CT_STRING, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,byte***) },
-
-#define ARRAY_ALLOC(type,len,val...) (type[]) { (type)len, ##val } + 1
- // creates an array with an allocated space in the front for the (Pascal-like) length
-#define ARRAY_LEN(a) *(uns*)(a-1)
- // length of the array
-
-/* Configuration items for sections, lists, and parsed items */
+#define CF_START_ITEMS .cfg = ( struct cf_item[] ) {
+#define CF_END_ITEMS { .cls = CC_END } },
+/* Configuration items */
struct clist;
-#define CF_PARSER(n,p,f,c) { .type = CT_PARSER, .name = n, .number = c, .ptr = p, .ptr2.par = (cf_parser*) f },
-#define CF_SECTION(n,p,s) { .type = CT_SECTION, .name = n, .number = 1, .ptr = p, .ptr2.sub = s },
-#define CF_LIST(n,p,s) { .type = CT_LIST, .name = n, .number = 1, .ptr = CHECK_PTR_TYPE(p,struct clist*), .ptr2.sub = s },
+#define CF_STATIC(n,p,T,t,c) { .cls = CC_STATIC, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,t*), .u.type = CT_##T },
+#define CF_DYNAMIC(n,p,T,t,c) { .cls = CC_DYNAMIC, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,t**), .u.type = CT_##T },
+#define CF_PARSER(n,p,f,c) { .cls = CC_PARSER, .name = n, .number = c, .ptr = p, .u.par = (cf_parser*) f },
+#define CF_SECTION(n,p,s) { .cls = CC_SECTION, .name = n, .number = 1, .ptr = p, .u.sub = s },
+#define CF_LIST(n,p,s) { .cls = CC_LIST, .name = n, .number = 1, .ptr = CHECK_PTR_TYPE(p,struct clist*), .u.sub = s },
+/* Configuration items for basic types */
+#define CF_INT(n,p) CF_STATIC(n,p,INT,int,1)
+#define CF_INT_ARY(n,p,c) CF_STATIC(n,p,INT,int,c)
+#define CF_INT_DYN(n,p,c) CF_DYNAMIC(n,p,INT,int,c)
+#define CF_U64(n,p) CF_STATIC(n,p,U64,u64,1)
+#define CF_U64_ARY(n,p,c) CF_STATIC(n,p,U64,u64,c)
+#define CF_U64_DYN(n,p,c) CF_DYNAMIC(n,p,U64,u64,c)
+#define CF_DOUBLE(n,p) CF_STATIC(n,p,DOUBLE,double,1)
+#define CF_DOUBLE_ARY(n,p,c) CF_STATIC(n,p,DOUBLE,double,c)
+#define CF_DOUBLE_DYN(n,p,c) CF_DYNAMIC(n,p,DOUBLE,double,c)
+#define CF_STRING(n,p) CF_STATIC(n,p,STRING,byte*,1)
+#define CF_STRING_ARY(n,p,c) CF_STATIC(n,p,STRING,byte*,c)
+#define CF_STRING_DYN(n,p,c) CF_DYNAMIC(n,p,STRING,byte*,c)
+
+#define DYN_LEN(a) *(uns*)(a-1)
+ // length of a dynamic array
+#define DYN_ALLOC(type,len,val...) (type[]) { (type)len, ##val } + 1
+ // creates a static instance of a dynamic array
/* Memory allocation */
void *cf_malloc(uns size);
byte *cf_printf(char *fmt, ...);
/* Undo journal for error recovery */
-uns cf_journal_active(uns flag);
void cf_journal_block(void *ptr, uns len);
#endif