From d33280a978c3176e5c589c9cd477b766204e4914 Mon Sep 17 00:00:00 2001 From: Peter Rajnoha Date: Thu, 29 May 2014 09:38:22 +0200 Subject: [PATCH] report: select: add _check_selection fn to support checking fields against given selections This is rebased and edited versions of the original design and patch proposed by Jun'ichi Nomura: http://www.redhat.com/archives/dm-devel/2007-April/msg00025.html The _check_selection implements the actual field checking against the selection tree. --- libdm/libdm-report.c | 156 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 1 deletion(-) diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c index 1d00c51bf..51224f189 100644 --- a/libdm/libdm-report.c +++ b/libdm/libdm-report.c @@ -16,6 +16,8 @@ #include "dmlib.h" #include +#include /* fabs() */ +#include /* DBL_EPSILON */ /* * Internal flags @@ -797,6 +799,157 @@ static void *_report_get_field_data(struct dm_report *rh, return (void *)(ret + rh->fields[fp->field_num].offset); } +static int _cmp_field_int(const char *field_id, uint64_t a, uint64_t b, uint32_t flags) +{ + switch(flags & FLD_CMP_MASK) { + case FLD_CMP_EQUAL: + return a == b; + case FLD_CMP_NOT|FLD_CMP_EQUAL: + return a != b; + case FLD_CMP_NUMBER|FLD_CMP_GT: + return a > b; + case FLD_CMP_NUMBER|FLD_CMP_GT|FLD_CMP_EQUAL: + return a >= b; + case FLD_CMP_NUMBER|FLD_CMP_LT: + return a < b; + case FLD_CMP_NUMBER|FLD_CMP_LT|FLD_CMP_EQUAL: + return a <= b; + default: + log_error(INTERNAL_ERROR "_cmp_field_int: unsupported number " + "comparison type for field %s", field_id); + } + + return 0; +} + +static int _close_enough(double d1, double d2) +{ + return fabs(d1 - d2) < DBL_EPSILON; +} + +static int _cmp_field_double(const char *field_id, double a, double b, uint32_t flags) +{ + switch(flags & FLD_CMP_MASK) { + case FLD_CMP_EQUAL: + return _close_enough(a, b); + case FLD_CMP_NOT|FLD_CMP_EQUAL: + return !_close_enough(a, b); + case FLD_CMP_NUMBER|FLD_CMP_GT: + return (a > b) && !_close_enough(a, b); + case FLD_CMP_NUMBER|FLD_CMP_GT|FLD_CMP_EQUAL: + return (a > b) || _close_enough(a, b); + case FLD_CMP_NUMBER|FLD_CMP_LT: + return (a < b) && !_close_enough(a, b); + case FLD_CMP_NUMBER|FLD_CMP_LT|FLD_CMP_EQUAL: + return a < b || _close_enough(a, b); + default: + log_error(INTERNAL_ERROR "_cmp_field_double: unsupported number " + "comparison type for selection field %s", field_id); + } + + return 0; +} + +static int _cmp_field_string(const char *field_id, const char *a, const char *b, uint32_t flags) +{ + switch (flags & FLD_CMP_MASK) { + case FLD_CMP_EQUAL: + return !strcmp(a, b); + case FLD_CMP_NOT|FLD_CMP_EQUAL: + return strcmp(a, b); + default: + log_error(INTERNAL_ERROR "_cmp_field_string: unsupported string " + "comparison type for selection field %s", field_id); + } + + return 0; +} + +static int _cmp_field_regex(const char *s, struct dm_regex *r, uint32_t flags) +{ + int match = dm_regex_match(r, s) >= 0; + return flags & FLD_CMP_NOT ? !match : match; +} + +static int _compare_selection_field(struct dm_report *rh, + struct dm_report_field *f, + struct field_selection *fs) +{ + const char *field_id = rh->fields[f->props->field_num].id; + int r = 0; + + if (!f->sort_value) { + log_error("_compare_selection_field: field without value :%d", + f->props->field_num); + return 0; + } + + if (fs->flags & FLD_CMP_REGEX) + r = _cmp_field_regex((const char *) f->sort_value, fs->v.r, fs->flags); + else { + switch(f->props->flags & DM_REPORT_FIELD_TYPE_MASK) { + case DM_REPORT_FIELD_TYPE_NUMBER: + r = _cmp_field_int(field_id, *(const uint64_t *) f->sort_value, fs->v.i, fs->flags); + break; + case DM_REPORT_FIELD_TYPE_SIZE: + r = _cmp_field_double(field_id, *(const uint64_t *) f->sort_value, fs->v.d, fs->flags); + break; + case DM_REPORT_FIELD_TYPE_STRING: + r = _cmp_field_string(field_id, (const char *) f->sort_value, fs->v.s, fs->flags); + break; + default: + log_error(INTERNAL_ERROR "_compare_selection_field: unknown field type for field %s", field_id); + } + } + + return r; +} + +static int _check_selection(struct dm_report *rh, struct selection_node *sn, + struct dm_list *fields) +{ + int r; + struct selection_node *iter_n; + struct dm_report_field *f; + + switch (sn->type & SEL_MASK) { + case SEL_ITEM: + r = 1; + dm_list_iterate_items(f, fields) { + if (sn->selection.item->fp != f->props) + continue; + if (!_compare_selection_field(rh, f, sn->selection.item)) + r = 0; + } + break; + case SEL_OR: + r = 0; + dm_list_iterate_items(iter_n, &sn->selection.set) + if ((r |= _check_selection(rh, iter_n, fields))) + break; + break; + case SEL_AND: + r = 1; + dm_list_iterate_items(iter_n, &sn->selection.set) + if (!(r &= _check_selection(rh, iter_n, fields))) + break; + break; + default: + log_error("Unsupported selection type"); + return 0; + } + + return (sn->type & SEL_MODIFIER_NOT) ? !r : r; +} + +static int _check_report_selection(struct dm_report *rh, struct dm_list *fields) +{ + if (!rh->selection_root) + return 1; + + return _check_selection(rh, rh->selection_root, fields); +} + int dm_report_object(struct dm_report *rh, void *object) { struct field_properties *fp; @@ -1356,7 +1509,8 @@ static struct selection_node *_parse_selection(struct dm_report *rh, if (!(last = _tok_value_regex(ft, last, &vs, &ve, &flags))) goto_bad; } else { - if (ft->flags == DM_REPORT_FIELD_TYPE_SIZE) + if (ft->flags == DM_REPORT_FIELD_TYPE_SIZE || + ft->flags == DM_REPORT_FIELD_TYPE_NUMBER) custom = &factor; else custom = NULL;