vici: Add generic callback based vici message parsing

This commit is contained in:
Martin Willi
2014-05-07 14:13:36 +02:00
parent 1f2e63ea41
commit dd5ce0a97a
2 changed files with 139 additions and 0 deletions
+95
View File
@@ -412,6 +412,100 @@ METHOD(vici_message_t, get_encoding, chunk_t,
return this->encoding;
}
/**
* Private parse context data
*/
struct vici_parse_context_t {
/** current section nesting level */
int level;
/** parse enumerator */
enumerator_t *e;
};
METHOD(vici_message_t, parse, bool,
private_vici_message_t *this, vici_parse_context_t *ctx,
vici_section_cb_t section, vici_value_cb_t kv, vici_value_cb_t li,
void *user)
{
vici_parse_context_t root = {};
char *name, *list = NULL;
vici_type_t type;
chunk_t value;
int base;
bool ok = TRUE;
if (!ctx)
{
ctx = &root;
root.e = create_enumerator(this);
}
base = ctx->level;
while (ok)
{
ok = ctx->e->enumerate(ctx->e, &type, &name, &value);
if (ok)
{
switch (type)
{
case VICI_KEY_VALUE:
if (ctx->level == base && kv)
{
name = strdup(name);
this->strings->insert_last(this->strings, name);
ok = kv(user, &this->public, name, value);
}
continue;
case VICI_LIST_START:
if (ctx->level == base)
{
list = strdup(name);
this->strings->insert_last(this->strings, list);
}
continue;
case VICI_LIST_ITEM:
if (list && li)
{
name = strdup(name);
this->strings->insert_last(this->strings, name);
ok = li(user, &this->public, list, value);
}
continue;
case VICI_LIST_END:
if (ctx->level == base)
{
list = NULL;
}
continue;
case VICI_SECTION_START:
if (ctx->level++ == base && section)
{
name = strdup(name);
this->strings->insert_last(this->strings, name);
ok = section(user, &this->public, ctx, name);
}
continue;
case VICI_SECTION_END:
if (ctx->level-- == base)
{
break;
}
continue;
case VICI_END:
break;
}
}
break;
}
if (ctx == &root)
{
root.e->destroy(root.e);
}
return ok;
}
METHOD(vici_message_t, dump, bool,
private_vici_message_t *this, char *label, FILE *out)
{
@@ -506,6 +600,7 @@ vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup)
.get_value = _get_value,
.vget_value = _vget_value,
.get_encoding = _get_encoding,
.parse = _parse,
.dump = _dump,
.destroy = _destroy,
},
+44
View File
@@ -24,6 +24,7 @@
#include <library.h>
typedef struct vici_message_t vici_message_t;
typedef struct vici_parse_context_t vici_parse_context_t;
typedef enum vici_type_t vici_type_t;
/**
@@ -47,6 +48,30 @@ enum vici_type_t {
VICI_END
};
/**
* Callback function for key/value and list items, invoked by parse().
*
* @param user user data, as passed to parse()
* @param message message currently parsing
* @param name name of key or list
* @param value parsed value
* @return TRUE if parsed successfully
*/
typedef bool (*vici_value_cb_t)(void *user, vici_message_t *message,
char *name, chunk_t value);
/**
* Callback function for sections, invoked by parse().
*
* @param user user data, as passed to parse()
* @param message message currently parsing
* @param ctx parse context, to pass to recursive parse() invocations.
* @param name name of the section
* @return TRUE if parsed successfully
*/
typedef bool (*vici_section_cb_t)(void *user, vici_message_t *message,
vici_parse_context_t *ctx, char *name);
/**
* Names for vici encoding types
*/
@@ -137,6 +162,25 @@ struct vici_message_t {
*/
chunk_t (*get_encoding)(vici_message_t *this);
/**
* Parse a message using callback functions.
*
* Any of the callbacks may be NULL to skip this kind of item. Callbacks are
* invoked for the current section level only. To descent into sections,
* call parse() from within a section callback using the provided parse
* context.
*
* @param ctx parse context, NULL for root level
* @param section callback invoked for each section
* @param kv callback invoked for key/value pairs
* @param li callback invoked for list items
* @param user user data to pass to callbacks
* @return TRUE if parsed successfully
*/
bool (*parse)(vici_message_t *this, vici_parse_context_t *ctx,
vici_section_cb_t section, vici_value_cb_t kv,
vici_value_cb_t li, void *user);
/**
* Dump a message text representation to a FILE stream.
*