ossp-pkg/lmtp2nntp/lmtp2nntp_val.c
#include "lmtp2nntp_val.h"
#include "lmtp2nntp_lh.h"
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
/* usually val_object_t.data is a pointer val_object_t.data.p, but VAL_INLINE
* signals val_object_t.data is actual data val_object_t.data.[csilfd]
*/
enum {
VAL_INLINE = 1<<31
};
typedef struct {
int type;
union {
val_t *v;
void *p;
char c;
short s;
int i;
long l;
float f;
double d;
} data;
char *desc;
} val_object_t;
struct val_s {
lh_t *lh;
};
static void *val_storage(val_object_t *obj)
{
void *storage;
if (obj->type & VAL_INLINE) {
switch (obj->type & ~VAL_INLINE) {
case VAL_TYPE_VAL:
storage = &obj->data.v;
break;
case VAL_TYPE_PTR:
storage = &obj->data.p;
break;
case VAL_TYPE_CHAR:
storage = &obj->data.c;
break;
case VAL_TYPE_SHORT:
storage = &obj->data.s;
break;
case VAL_TYPE_INT:
storage = &obj->data.i;
break;
case VAL_TYPE_LONG:
storage = &obj->data.l;
break;
case VAL_TYPE_FLOAT:
storage = &obj->data.f;
break;
case VAL_TYPE_DOUBLE:
storage = &obj->data.d;
break;
default:
storage = NULL;
}
}
else
storage = obj->data.p;
return storage;
}
val_rc_t val_create(val_t **valp)
{
val_t *val;
if (valp == NULL)
return VAL_ERR_ARG;
if ((val = (val_t *)malloc(sizeof(val_t))) == NULL)
return VAL_ERR_SYS;
if ((val->lh = lh_create()) == NULL) {
free(val);
return VAL_ERR_SYS;
}
*valp = val;
return VAL_OK;
}
static int (val_destroy_cb)(void *_ctx, const void *keyptr, int keylen, const void *datptr, int datlen)
{
val_object_t *obj;
obj = (val_object_t *)datptr;
if (obj->desc != NULL)
free(obj->desc);
return TRUE;
}
val_rc_t val_destroy(val_t *val)
{
if (val == NULL)
return VAL_ERR_ARG;
lh_apply(val->lh, val_destroy_cb, NULL);
if (!lh_destroy(val->lh))
return VAL_ERR_SYS;
free(val);
return VAL_OK;
}
val_rc_t val_reg(val_t *val, const char *name, int type, const char *desc, void *storage)
{
val_object_t *obj;
val_object_t newobj;
const char *cp;
val_t *child;
if (val == NULL || name == NULL || type == 0)
return VAL_ERR_ARG;
if ((cp = strchr(name, '.')) != NULL) {
if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL))
return VAL_ERR_ARG;
if (!(obj->type & VAL_TYPE_VAL))
return VAL_ERR_USE;
child = *(val_t **)(val_storage(obj));
return val_reg(child, cp+1, type, desc, storage);
}
if (desc != NULL)
newobj.desc = strdup(desc);
else
newobj.desc = NULL;
if (storage == NULL) {
newobj.type = (type | VAL_INLINE);
newobj.data.l = 0;
}
else {
newobj.type = (type & ~VAL_INLINE);
newobj.data.p = storage;
}
if (!lh_insert(val->lh, name, strlen(name), &newobj, sizeof(newobj), 1))
return VAL_ERR_SYS;
return VAL_OK;
}
val_rc_t val_vset(val_t *val, const char *name, va_list ap)
{
val_object_t *obj;
void *storage;
const char *cp;
val_t *child;
if (val == NULL || name == NULL || ap == NULL)
return VAL_ERR_ARG;
if ((cp = strchr(name, '.')) != NULL) {
if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL))
return VAL_ERR_ARG;
if (!(obj->type & VAL_TYPE_VAL))
return VAL_ERR_USE;
child = *(val_t **)(val_storage(obj));
return val_vset(child, cp+1, ap);
}
if (!lh_lookup(val->lh, name, strlen(name), (void **)&obj, NULL))
return VAL_ERR_ARG;
storage = val_storage(obj);
switch (obj->type & ~VAL_INLINE) {
case VAL_TYPE_VAL:
*(val_t **)storage = (val_t *)va_arg(ap, void *);
break;
case VAL_TYPE_PTR:
*(char **)storage = (char *)va_arg(ap, void *);
break;
case VAL_TYPE_CHAR:
*(char *)storage = (char)va_arg(ap, int);
break;
case VAL_TYPE_SHORT:
*(short *)storage = (short)va_arg(ap, int);
break;
case VAL_TYPE_INT:
*(int *)storage = (int)va_arg(ap, int);
break;
case VAL_TYPE_LONG:
*(long *)storage = (long)va_arg(ap, long);
break;
case VAL_TYPE_FLOAT:
*(float *)storage = (float)va_arg(ap, double);
break;
case VAL_TYPE_DOUBLE:
*(double *)storage = (double)va_arg(ap, double);
break;
}
return VAL_OK;
}
val_rc_t val_set(val_t *val, const char *name, ...)
{
val_rc_t rc;
va_list ap;
if (val == NULL || name == NULL)
return VAL_ERR_ARG;
va_start(ap, name);
rc = val_vset(val, name, ap);
va_end(ap);
return rc;
}
val_rc_t val_vget(val_t *val, const char *name, va_list ap)
{
val_object_t *obj;
void *storage;
const char *cp;
val_t *child;
if (val == NULL || name == NULL || ap == NULL)
return VAL_ERR_ARG;
if ((cp = strchr(name, '.')) != NULL) {
if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL))
return VAL_ERR_ARG;
if (!(obj->type & VAL_TYPE_VAL))
return VAL_ERR_USE;
child = *(val_t **)(val_storage(obj));
return val_vget(child, cp+1, ap);
}
if (!lh_lookup(val->lh, name, strlen(name), (void **)&obj, NULL))
return VAL_ERR_ARG;
storage = val_storage(obj);
switch (obj->type & ~VAL_INLINE) {
case VAL_TYPE_VAL:
*((val_t **)va_arg(ap, void *)) = *(val_t **)storage;
break;
case VAL_TYPE_PTR:
*((char **)va_arg(ap, void *)) = *(char **)storage;
break;
case VAL_TYPE_CHAR:
*((char *)va_arg(ap, int *)) = *(char *)storage;
break;
case VAL_TYPE_SHORT:
*((short *)va_arg(ap, int *)) = *(short *)storage;
break;
case VAL_TYPE_INT:
*((int *)va_arg(ap, int *)) = *(int *)storage;
break;
case VAL_TYPE_LONG:
*((long *)va_arg(ap, long *)) = *(long *)storage;
break;
case VAL_TYPE_FLOAT:
*((float *)va_arg(ap, double *)) = *(float *)storage;
break;
case VAL_TYPE_DOUBLE:
*((double *)va_arg(ap, double *)) = *(double *)storage;
break;
}
return VAL_OK;
}
val_rc_t val_get(val_t *val, const char *name, ...)
{
val_rc_t rc;
va_list ap;
if (val == NULL || name == NULL)
return VAL_ERR_ARG;
va_start(ap, name);
rc = val_vget(val, name, ap);
va_end(ap);
return rc;
}
#define VAL_MAXNAME 1024
typedef struct {
val_t *val;
char *name;
int prefixlen;
int depth;
val_cb_t cb;
void *ctx;
val_rc_t rc;
} val_apply_ctx_t;
static val_rc_t val_apply_internal(val_t *, const char *, int, int, val_cb_t, void *);
static int (val_apply_cb)(void *_ctx, const void *keyptr, int keylen, const void *datptr, int datlen)
{
val_apply_ctx_t *ctx = (val_apply_ctx_t *)_ctx;
char name[VAL_MAXNAME+1];
int prefixlen;
/* on-the-fly create NUL-terminated name string */
if ((strlen(ctx->name) + 1 + keylen) > VAL_MAXNAME) {
ctx->rc = VAL_ERR_MEM;
return FALSE;
}
if (strlen(ctx->name) > 0) {
strcpy(name, ctx->name);
strcat(name, ".");
prefixlen = ctx->prefixlen + 1;
}
else {
*name = '\0';
prefixlen = ctx->prefixlen;
}
strncat(name, (char *)keyptr, keylen);
if ((ctx->rc = val_apply_internal(ctx->val, name, prefixlen, ctx->depth, ctx->cb, ctx->ctx)) != VAL_OK)
return FALSE;
return TRUE;
}
static val_rc_t val_apply_internal(val_t *val, const char *name, int prefixlen, int depth, val_cb_t cb, void *ctx)
{
val_object_t *obj;
val_t *child;
char *cp;
val_rc_t rc;
val_apply_ctx_t val_ctx;
fprintf(stderr, "DEBUG: val_apply_internal name=<%s>, prefixlen=%d, depth=%d\n", name, prefixlen, depth);
if (name[prefixlen] == '\0') {
/* prefix="foo.bar.", remainder="" */
//if (--depth > 0) {
val_ctx.val = val;
val_ctx.name = (char *)name;
val_ctx.prefixlen = prefixlen;
val_ctx.depth = depth;
val_ctx.cb = cb;
val_ctx.ctx = ctx;
val_ctx.rc = VAL_OK;
if (!lh_apply(val->lh, val_apply_cb, &val_ctx))
return VAL_ERR_SYS;
//}
}
else {
if ((cp = strchr(name+prefixlen, '.')) != NULL) {
/* prefix="foo.bar.", remainder="quux.baz" */
if (!lh_lookup(val->lh, name+prefixlen, cp-(name+prefixlen), (void **)&obj, NULL))
return VAL_ERR_ARG;
if (!(obj->type & VAL_TYPE_VAL))
return VAL_ERR_USE;
child = *(val_t **)(val_storage(obj));
if (depth == 0)
return VAL_OK;
return val_apply_internal(child, name, cp-name+1, --depth, cb, ctx);
}
else {
/* prefix="foo.bar.quux.", remainder="baz" */
if (!lh_lookup(val->lh, name+prefixlen, strlen(name+prefixlen), (void **)&obj, NULL))
return VAL_ERR_ARG;
/* execute VAL callback */
if ((rc = cb(ctx, name, (obj->type & ~VAL_INLINE), obj->desc, val_storage(obj))) != VAL_OK)
return rc;
//fprintf(stderr, "DEBUG: depth=%d obj->type=%.8lx\n", depth, obj->type);
if (obj->type & VAL_TYPE_VAL) {
if (depth == 0)
return VAL_OK;
child = *(val_t **)(val_storage(obj));
return val_apply_internal(child, name, strlen(name), --depth, cb, ctx);
}
}
}
return VAL_OK;
}
val_rc_t val_apply(val_t *val, const char *name, int depth, val_cb_t cb, void *ctx)
{
if (val == NULL || name == NULL || depth < 0 || cb == NULL)
return VAL_ERR_ARG;
return val_apply_internal(val, name, 0, depth, cb, ctx);
}