static struct clist secs;
static time_t t1, t2;
static u32 ip;
-static int look[2] = {2, 1};
+static int *look = DARY_ALLOC(int, 2, 2, 1);
+static u16 numbers[10] = { 2, 100, 1, 5 };
+
+static byte *
+parse_u16(byte *string, u16 *ptr)
+{
+ uns a;
+ byte *msg = cf_parse_int(string, &a);
+ if (msg)
+ return msg;
+ if (a >= (1<<16))
+ return "Come on, man, this doesn't fit to 16 bits";
+ *ptr = a;
+ return NULL;
+}
+
+static void
+dump_u16(struct fastbuf *fb, u16 *ptr)
+{
+ bprintf(fb, "%d ", *ptr);
+}
+
+static struct cf_user_type u16_type = {
+ .size = sizeof(u16),
+ .parser = (cf_parser1*) parse_u16,
+ .dumper = (cf_dumper1*) dump_u16
+};
static byte *
init_top(void *ptr UNUSED)
CF_SECTION("master", &sec1, &cf_sec_1),
CF_LIST("slaves", &secs, &cf_sec_1),
CF_IP("ip", &ip),
- CF_LOOKUP_ARY("look", look, alphabet, 2),
+ CF_LOOKUP_DYN("look", &look, alphabet, 1000),
+ CF_USER_ARY("numbers", numbers, &u16_type, 10),
CF_END
}
};
{ sizeof(double), cf_parse_double },
{ sizeof(u32), cf_parse_ip },
{ sizeof(byte*), cf_parse_string },
- { sizeof(int), NULL } // lookups are parsed extra
+ { sizeof(int), NULL }, // lookups are parsed extra
+ { 0, NULL }, // user-defined types are parsed extra
};
+static inline uns
+type_size(enum cf_type type, struct cf_user_type *utype)
+{
+ if (type < CT_USER)
+ return parsers[type].size;
+ else
+ return utype->size;
+}
+
static byte *
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;
+ uns size = type_size(type, u->utype);
if (type < CT_LOOKUP)
- msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * parsers[type].size);
+ 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 * sizeof(int), u->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
ASSERT(0);
if (msg)
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;
+ uns size = type_size(item->type, item->u.utype);
+ *ptr = cf_malloc((number+1) * size) + size;
+ * (uns*) (*ptr - size) = number;
return cf_parse_ary(number, pars, *ptr, type, &item->u);
}
{
enum cf_type type = item->type;
void *old_p = *ptr;
- int old_nr = * (int*) (old_p - parsers[type].size);
+ uns size = type_size(item->type, item->u.utype);
+ int old_nr = * (int*) (old_p - size);
int taken = MIN(number, item->number-old_nr);
*processed = taken;
// stretch the dynamic array
- void *new_p = cf_malloc((old_nr + taken + 1) * parsers[type].size) + parsers[type].size;
- * (uns*) (new_p - parsers[type].size) = old_nr + taken;
+ void *new_p = cf_malloc((old_nr + taken + 1) * size) + size;
+ * (uns*) (new_p - size) = old_nr + taken;
cf_journal_block(ptr, sizeof(void*));
*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, &item->u);
+ memcpy(new_p, old_p, old_nr * size);
+ return cf_parse_ary(taken, pars, new_p + old_nr * size, type, &item->u);
} else if (op == OP_PREPEND) {
- memcpy(new_p + taken * parsers[type].size, old_p, old_nr * parsers[type].size);
+ memcpy(new_p + taken * size, old_p, old_nr * size);
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->type].size);
+ uns size = type_size(item->type, item->u.utype);
+ cf_journal_block(ptr, taken * size);
return cf_parse_ary(taken, pars, ptr, item->type, &item->u);
case CC_DYNAMIC:
if (!allow_dynamic)
if (item->type == CT_STRING)
return strcmp(* (byte**) i1, * (byte**) i2);
else // all numeric types
- return memcmp(i1, i2, parsers[item->type].size);
+ return memcmp(i1, i2, type_size(item->type, item->u.utype));
}
static void *
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;
+ case CT_USER:
+ if (u->utype->dumper)
+ u->utype->dumper(fb, ptr);
+ else
+ bprintf(fb, "??? ");
+ break;
}
}
{
ptr += (addr_int_t) item->ptr;
enum cf_type type = item->type;
+ uns size = type_size(item->type, item->u.utype);
int i;
spaces(fb, level);
bprintf(fb, "%s: c%d #%d ", item->name, item->cls, item->number);
if (item->cls == CC_STATIC || item->cls == CC_DYNAMIC)
bprintf(fb, "t%d ", type);
if (item->cls == CC_STATIC) {
+ if (item->type == CT_USER)
+ bprintf(fb, "S%d ", size);
for (i=0; i<item->number; i++)
- dump_basic(fb, ptr + i * parsers[type].size, type, &item->u);
+ dump_basic(fb, ptr + i * size, type, &item->u);
} else if (item->cls == CC_DYNAMIC) {
+ if (item->type == CT_USER)
+ bprintf(fb, "S%d ", size);
ptr = * (void**) ptr;
if (ptr) {
- int real_nr = * (int*) (ptr - parsers[type].size);
+ int real_nr = * (int*) (ptr - size);
bprintf(fb, "##%d ", real_nr);
for (i=0; i<real_nr; i++)
- dump_basic(fb, ptr + i * parsers[type].size, type, &item->u);
+ dump_basic(fb, ptr + i * size, type, &item->u);
} else
bprintf(fb, "NULL ");
}
CT_INT, CT_U64, CT_DOUBLE, // number types
CT_IP, // IP address
CT_STRING, // string type
- CT_LOOKUP // in a string table
+ CT_LOOKUP, // in a string table
+ CT_USER // user-defined type
};
+struct fastbuf;
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_parser1(byte *string, void *ptr);
+ /* A parser function for user-defined types get one string and pointer to the
+ * destination variable. It can only store the value inside [ptr,ptr+size),
+ * where size is fixed for each type. It does not have to call
+ * cf_journal_block(). */
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
* checks and postprocess the parsed values. Commit-hooks must call
* cf_journal_block() too. Caveat! init-hooks for static sections must not
* use cf_malloc() but normal xmalloc(). */
+typedef void *cf_dumper1(struct fastbuf *fb, void *ptr);
+ /* Dumps the contents of a variable of a user-defined type. */
+
+struct cf_user_type {
+ uns size; // of the parsed attribute
+ cf_parser1 *parser; // how to parse it
+ cf_dumper1 *dumper; // optional and for debugging purposes only
+};
struct cf_section;
struct cf_item {
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
+ struct cf_user_type *utype; // specification of the user-defined type
} u;
enum cf_class cls:16; // attribute class
enum cf_type type:16; // type of a static or dynamic attribute
#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 }
+#define CF_USER(n,p,t) { .cls = CC_STATIC, .type = CT_USER, .name = n, .number = 1, .ptr = p, .u.utype = t }
+#define CF_USER_ARY(n,p,t,c) { .cls = CC_STATIC, .type = CT_USER, .name = n, .number = c, .ptr = p, .u.utype = t }
+#define CF_USER_DYN(n,p,t,c) { .cls = CC_DYNAMIC, .type = CT_USER, .name = n, .number = c, .ptr = p, .u.utype = t }
+ // Beware that CF_USER_DYN can only be used on user-defined types of size at least 4
/* If you aren't picky about the number of parameters */
#define CF_ANY_NUM -0x7fffffff
enum cf_operation { CF_OPERATIONS };
#undef T
-struct fastbuf;
byte *cf_find_item(byte *name, struct cf_item *item);
byte *cf_write_item(struct cf_item *item, enum cf_operation op, int number, byte **pars);
void cf_dump_sections(struct fastbuf *fb);