return NULL;
}
+static byte *
+cf_parse_lookup(byte *str, int *ptr, char **t)
+{
+ char **n = t;
+ uns total_len = 0;
+ while (*n && strcasecmp(*n, str)) {
+ total_len += strlen(*n) + 2;
+ n++;
+ }
+ if (*n) {
+ *ptr = n - t;
+ return NULL;
+ }
+ byte *err = cf_malloc(total_len + strlen(str) + 60), *c = err;
+ c += sprintf(err, "Invalid value %s, possible values are: ", str);
+ for (n=t; *n; n++)
+ c+= sprintf(c, "%s, ", *n);
+ *ptr = -1;
+ return err;
+}
+
/* Register size of and parser for each basic type */
typedef byte *cf_basic_parser(byte *str, void *ptr);
{ sizeof(u64), cf_parse_u64 },
{ sizeof(double), cf_parse_double },
{ sizeof(u32), cf_parse_ip },
- { sizeof(byte*), cf_parse_string }
+ { sizeof(byte*), cf_parse_string },
+ { sizeof(int), NULL } // lookups are parsed extra
};
static byte *
-cf_parse_ary(uns number, byte **pars, void *ptr, enum cf_type type)
+cf_parse_ary(uns number, byte **pars, void *ptr, enum cf_type type, union cf_union *u)
{
for (uns i=0; i<number; i++)
{
- byte *msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * parsers[type].size);
+ byte *msg;
+ if (type < CT_LOOKUP)
+ msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * parsers[type].size);
+ else if (type == CT_LOOKUP)
+ msg = cf_parse_lookup(pars[i], ptr + i * sizeof(int), u->lookup);
+ else
+ ASSERT(0);
if (msg)
return cf_printf("Cannot parse item %d: %s", i+1, msg);
}
static byte *
interpret_set_dynamic(struct cf_item *item, int number, byte **pars, void **ptr)
{
- enum cf_type type = item->u.type;
+ enum cf_type type = item->type;
cf_journal_block(ptr, sizeof(void*));
// boundary checks done by the caller
*ptr = cf_malloc((number+1) * parsers[type].size) + parsers[type].size;
* (uns*) (*ptr - parsers[type].size) = number;
- return cf_parse_ary(number, pars, *ptr, type);
+ return cf_parse_ary(number, pars, *ptr, type, &item->u);
}
static byte *
interpret_add_dynamic(struct cf_item *item, int number, byte **pars, int *processed, void **ptr, enum cf_operation op)
{
- enum cf_type type = item->u.type;
+ enum cf_type type = item->type;
void *old_p = *ptr;
int old_nr = * (int*) (old_p - parsers[type].size);
int taken = MIN(number, item->number-old_nr);
*ptr = new_p;
if (op == OP_APPEND) {
memcpy(new_p, old_p, old_nr * parsers[type].size);
- return cf_parse_ary(taken, pars, new_p + old_nr * parsers[type].size, type);
+ return cf_parse_ary(taken, pars, new_p + old_nr * parsers[type].size, type, &item->u);
} else if (op == OP_PREPEND) {
memcpy(new_p + taken * parsers[type].size, old_p, old_nr * parsers[type].size);
- return cf_parse_ary(taken, pars, new_p, type);
+ return cf_parse_ary(taken, pars, new_p, type, &item->u);
} else
return cf_printf("Dynamic arrays do not support operation %s", op_names[op]);
}
return "Missing value";
taken = MIN(number, item->number);
*processed = taken;
- cf_journal_block(ptr, taken * parsers[item->u.type].size);
- return cf_parse_ary(taken, pars, ptr, item->u.type);
+ cf_journal_block(ptr, taken * parsers[item->type].size);
+ return cf_parse_ary(taken, pars, ptr, item->type, &item->u);
case CC_DYNAMIC:
if (!allow_dynamic)
return "Dynamic array cannot be used here";
ASSERT(item->cls == CC_STATIC);
i1 += (addr_int_t) item->ptr;
i2 += (addr_int_t) item->ptr;
- if (item->u.type == CT_STRING)
+ if (item->type == CT_STRING)
return strcmp(* (byte**) i1, * (byte**) i2);
else // all numeric types
- return memcmp(i1, i2, parsers[item->u.type].size);
+ return memcmp(i1, i2, parsers[item->type].size);
}
static void *
}
static void
-dump_basic(struct fastbuf *fb, void *ptr, enum cf_type type)
+dump_basic(struct fastbuf *fb, void *ptr, enum cf_type type, union cf_union *u)
{
switch (type) {
case CT_INT: bprintf(fb, "%d ", *(uns*)ptr); break;
case CT_DOUBLE: bprintf(fb, "%lg ", *(double*)ptr); break;
case CT_IP: bprintf(fb, "%08x ", *(uns*)ptr); break;
case CT_STRING: bprintf(fb, "'%s' ", *(byte**)ptr); break;
+ case CT_LOOKUP: bprintf(fb, "%s ", *(int*)ptr >= 0 ? u->lookup[ *(int*)ptr ] : "???"); break;
}
}
dump_item(struct fastbuf *fb, struct cf_item *item, int level, void *ptr)
{
ptr += (addr_int_t) item->ptr;
- enum cf_type type = item->u.type;
+ enum cf_type type = item->type;
int i;
spaces(fb, level);
bprintf(fb, "%s: c%d #%d ", item->name, item->cls, item->number);
bprintf(fb, "t%d ", type);
if (item->cls == CC_STATIC) {
for (i=0; i<item->number; i++)
- dump_basic(fb, ptr + i * parsers[type].size, type);
+ dump_basic(fb, ptr + i * parsers[type].size, type, &item->u);
} else if (item->cls == CC_DYNAMIC) {
ptr = * (void**) ptr;
if (ptr) {
int real_nr = * (int*) (ptr - parsers[type].size);
bprintf(fb, "##%d ", real_nr);
for (i=0; i<real_nr; i++)
- dump_basic(fb, ptr + i * parsers[type].size, type);
+ dump_basic(fb, ptr + i * parsers[type].size, type, &item->u);
} else
bprintf(fb, "NULL ");
}
enum cf_type {
CT_INT, CT_U64, CT_DOUBLE, // number types
CT_IP, // IP address
- CT_STRING // string type
+ CT_STRING, // string type
+ CT_LOOKUP // in a string table
};
typedef byte *cf_parser(uns number, byte **pars, void *ptr);
struct cf_section;
struct cf_item {
- enum cf_class cls;
- byte *name;
+ byte *name; // case insensitive
int number; // length of an array or #parameters of a parser (negative means at most)
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
+ union cf_union {
struct cf_section *sec; // declaration of a section or a list
cf_parser *par; // parser function
+ char **lookup; // NULL-terminated sequence of allowed strings for lookups
} u;
+ enum cf_class cls:16; // attribute class
+ enum cf_type type:16; // type of a static or dynamic attribute
};
struct cf_section {
#define CF_END { .cls = CC_END }
/* Configuration items */
struct clist;
-#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_STATIC(n,p,T,t,c) { .cls = CC_STATIC, .type = CT_##T, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,t*) }
+#define CF_DYNAMIC(n,p,T,t,c) { .cls = CC_DYNAMIC, .type = CT_##T, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,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.sec = s }
#define CF_LIST(n,p,s) { .cls = CC_LIST, .name = n, .number = 1, .ptr = CHECK_PTR_TYPE(p,struct clist*), .u.sec = s }
#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 CF_LOOKUP(n,p,t) { .cls = CC_STATIC, .type = CT_LOOKUP, .name = n, .number = 1, .ptr = CHECK_PTR_TYPE(p,int*), .u.lookup = t }
+#define CF_LOOKUP_ARY(n,p,t,c) { .cls = CC_STATIC, .type = CT_LOOKUP, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,int*), .u.lookup = t }
+#define CF_LOOKUP_DYN(n,p,t,c) { .cls = CC_DYNAMIC, .type = CT_LOOKUP, .name = n, .number = c, .ptr = CHECK_PTR_TYPE(p,int**), .u.lookup = t }
/* If you aren't picky about the number of parameters */
#define CF_ANY_NUM -0x7fffffff