#include "lmtp2nntp_val.h" #include "lmtp2nntp_lh.h" #include #include #include #include /* 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); }