[PATCH] libdevmapper: (4/6) Add filtering feature to dm_report

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

This patch adds a filtering feature to dm_report.

  - Add dm_report_set_filter() API.
    By calling this, dm_report_object() omits rows which doesn't
    match the condition specified by the filter.

  - Fitlers can be composed with the following components:
      * Simple comparison of field value
          '==', '!=', '>', '>=', '<', '<='
          Strings should be quoted by either ' or ".
          Only decimal, non-negative integers are currently allowed.
      * Regular expression
          '=~', '!~'
          Regular expression can be quoted by '/', '|', '#', or any
          other characters.
      * Logical combinations of the expressions
          'and', 'or', '!', '(', ')'

Thanks,
-- 
Jun'ichi Nomura, NEC Corporation of America
Add filtering feature to dm_report.

Filter is set by dm_report_set_filters()
---
 lib/.exported_symbols |    1 
 lib/libdevmapper.h    |    4 
 lib/libdm-report.c    |  783 +++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 781 insertions(+), 7 deletions(-)

Index: device-mapper.work/lib/.exported_symbols
===================================================================
--- device-mapper.work.orig/lib/.exported_symbols
+++ device-mapper.work/lib/.exported_symbols
@@ -127,5 +127,6 @@ dm_report_field_int32
 dm_report_field_uint32
 dm_report_field_uint64
 dm_report_field_set_value
+dm_report_set_filter
 dm_regex_create
 dm_regex_match
Index: device-mapper.work/lib/libdevmapper.h
===================================================================
--- device-mapper.work.orig/lib/libdevmapper.h
+++ device-mapper.work/lib/libdevmapper.h
@@ -688,6 +688,10 @@ int dm_report_object(struct dm_report *r
 int dm_report_output(struct dm_report *rh);
 void dm_report_free(struct dm_report *rh);
 
+/* Set filter */
+int dm_report_set_filter(struct dm_report *rh,
+			 const char *filter, uint32_t flags);
+
 /*
  * Report functions are provided for simple data types.
  * They take care of allocating copies of the data.
Index: device-mapper.work/lib/libdm-report.c
===================================================================
--- device-mapper.work.orig/lib/libdm-report.c
+++ device-mapper.work/lib/libdm-report.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2007 NEC Corporation
  *
  * This file is part of device-mapper userspace tools.
  * The code is based on LVM2 report function.
@@ -14,6 +15,7 @@
  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include <ctype.h>
 #include "libdevmapper.h"
 #include "list.h"
 #include "log.h"
@@ -24,6 +26,7 @@
 #define RH_SORT_REQUIRED	0x00000100
 #define RH_HEADINGS_PRINTED	0x00000200
 
+struct filter_node;
 struct dm_report {
 	struct dm_pool *mem;
 
@@ -46,6 +49,8 @@ struct dm_report {
 
 	/* To store caller private data */
 	void *private;
+
+	struct filter_node *filter_root;
 };
 
 /*
@@ -474,6 +479,653 @@ static int _parse_keys(struct dm_report 
 	return 1;
 }
 
+/*
+ * Filter tokens
+ */
+
+/*
+ * Comparison and logical operation tokens
+ *   OP_CMP := '==' | '!=' | '>' | '>=' | '<' | '<=' | '=~' | '!~'
+ *   OP_LOG := '!' | '(' | ')' | ',' | 'and' | 'or' | '&&' | '||'
+ */
+
+struct op_def {
+	const char *string;
+	uint32_t flags;
+	const char *desc;
+};
+
+static const char * _skip_space(const char *s)
+{
+	while (*s && isspace(*s))
+		s++;
+	return s;
+}
+
+static const char * _token_match(const char *s, const char *token)
+{
+	s = _skip_space(s);
+
+	if (!strncmp(s, token, strlen(token)))
+		return s + strlen(token);
+
+	return NULL;
+}
+
+static int _tok_op(struct op_def *t, const char *s, const char **end,
+		   uint32_t expect)
+{
+	const char *next;
+
+	for (; t->string; t++) {
+ 		if (expect && !(t->flags & expect))
+			continue;
+
+		if ((next = _token_match(s, t->string))) {
+			*end = next;
+			return t->flags;
+		}
+	}
+
+	*end = s;
+	return 0;
+}
+
+/* OP_CMP definition and matcher function */
+
+#define FLD_CMP_MASK	0x000FF000
+#define FLD_CMP_EQUAL	0x00001000
+#define FLD_CMP_NOT	0x00002000
+#define FLD_CMP_GT	0x00004000
+#define FLD_CMP_LT	0x00008000
+#define FLD_CMP_REGEX	0x00010000
+
+/* _tok_op() tries to match from the first of this list.
+ * So longer one should come first.
+ * e.g. ">=" should appear earlier in the list than ">". */
+static struct op_def _op_cmp[] = {
+	{ "==", FLD_CMP_EQUAL, "Equal to" },
+	{ "!=", FLD_CMP_NOT|FLD_CMP_EQUAL, "Not equal" },
+	{ ">=", FLD_CMP_GT|FLD_CMP_EQUAL|DM_REPORT_FIELD_TYPE_NUMBER,
+	  "Greater than or equal to" },
+	{ ">", FLD_CMP_GT|DM_REPORT_FIELD_TYPE_NUMBER, "Greater than" },
+	{ "<=", FLD_CMP_LT|FLD_CMP_EQUAL|DM_REPORT_FIELD_TYPE_NUMBER,
+	  "Lesser than or equal to" },
+	{ "<", FLD_CMP_LT|DM_REPORT_FIELD_TYPE_NUMBER, "Lesser than" },
+	{ "=~", FLD_CMP_REGEX|DM_REPORT_FIELD_TYPE_STRING,
+	  "Matching regular expression" },
+	{ "!~", FLD_CMP_REGEX|FLD_CMP_NOT|DM_REPORT_FIELD_TYPE_STRING,
+	  "Not matching regular expression" },
+	{ NULL, 0, NULL }
+};
+
+static uint32_t _tok_op_cmp(const char *s, const char **end)
+{
+	return _tok_op(_op_cmp, s, end, 0);
+}
+
+/* OP_LOG definitions and matcher functions */
+
+#define FILTER_TYPE_MASK	0x00FF
+#define FILTER_COND	0x0001
+#define FILTER_AND	0x0002
+#define FILTER_OR	0x0004
+#define FILTER_MODIFIER_MASK	0x0F00
+#define FILTER_NOT	0x0100
+#define FILTER_DELIMITER_MASK	0xF000
+#define FILTER_PS	0x1000
+#define FILTER_PE	0x2000
+
+static struct op_def _op_log[] = {
+	{ "&&",  FILTER_AND, NULL },
+	{ "and", FILTER_AND, NULL },
+	{ ",",   FILTER_AND, NULL },
+	{ "||",  FILTER_OR,  NULL },
+	{ "or",  FILTER_OR,  NULL },
+	{ "!",   FILTER_NOT, NULL },
+	{ "(",   FILTER_PS,  NULL },
+	{ ")",   FILTER_PE,  NULL },
+	{ NULL,  0, NULL},
+};
+
+static int _tok_op_log(const char *s, const char **end, uint32_t expect)
+{
+	return _tok_op(_op_log, s, end, expect);
+}
+
+/*
+ * Other tokens (FIELD, VALUE, STRING, NUMBER, REGEX)
+ *     FIELD := <strings of alphabet, number and '_'>
+ *     VALUE := NUMBER | STRING
+ *     REGEX := <strings quoted by any character>
+ *     NUMBER := <strings of [0-9]> (because sort_value is unsigned)
+ *     STRING := <strings quoted by '"' or '\''>
+ *
+ * _tok_* functions
+ *
+ *   Input:
+ *     s             - a pointer to the parsed string
+ *   Output:
+ *     begin         - a pointer to the beginning of the token
+ *     end           - a pointer to the end of the token + 1
+ *                     or undefined if return value is NULL
+ *     return value  - a starting point of the next parsing
+ *                     NULL if s doesn't match with token type
+ *                     (the parsing should be terminated)
+ */
+
+static const char * _tok_number(const char *s,
+			        const char **begin, const char **end)
+{
+	*begin = s;
+	while (*s && isdigit(*s))
+		s++;
+	*end = s;
+
+	return s;
+}
+
+static const char * _tok_string(const char *s,
+			        const char **begin, const char **end,
+			        const char endchar)
+{
+	*begin = s;
+	while (*s && *s != endchar)
+		s++;
+	*end = s;
+
+	return s;
+}
+
+static const char * _tok_regex(const char *s,
+			       const char **begin, const char **end,
+			       char *quote)
+{
+	s = _skip_space(s);
+
+	if (!*s) {
+		log_error("Regular expression expected");
+		return NULL;
+	}
+
+	switch (*s) {
+		case '(': *quote = ')'; break;
+		case '{': *quote = '}'; break;
+		case '[': *quote = ']'; break;
+		default:  *quote = *s;
+	}
+
+	s = _tok_string(s + 1, begin, end, *quote);
+	if (!*s) {
+		log_error("Missing end quote of regex");
+		return NULL;
+	}
+	s++;
+
+	return s;
+}
+
+static const char * _tok_value(const char *s,
+			       const char **begin, const char **end,
+			       char *quote)
+{
+	s = _skip_space(s);
+
+	if (*s == '"' || *s == '\'') { /* quoted string */
+		*quote = *s;
+		s = _tok_string(s + 1, begin, end, *quote);
+		if (!*s) {
+			log_error("Missing end quote of string");
+			return NULL;
+		}
+		s++;
+	} else { /* number */
+		*quote = 0;
+		s = _tok_number(s, begin, end);
+		if (*begin == *end) {
+			log_error("Empty value or unquoted string");
+			return NULL;
+		}
+	}
+
+	return s;
+}
+
+static int _field_name_char(const char c)
+{
+	return (isalnum(c) || c == '_' || c == '-');
+}
+
+static const char * _tok_field_name(const char *s,
+				    const char **begin, const char **end)
+{
+	s = _skip_space(s);
+
+	*begin = s;
+	while (*s && _field_name_char(*s))
+		s++;
+	*end = s;
+
+	if (*begin == *end)
+		return NULL;
+
+	return s;
+}
+
+static void _display_operators(void)
+{
+	int i;
+
+	log_print(" ");
+	log_print("Comparison operators");
+	log_print("--------------------");
+
+	for (i = 0; _op_cmp[i].string; i++)
+		log_print("  %-4s - %s%s%s", _op_cmp[i].string, _op_cmp[i].desc,
+			_op_cmp[i].flags & DM_REPORT_FIELD_TYPE_NUMBER ?
+				" (numeric field only)" : "",
+			_op_cmp[i].flags & DM_REPORT_FIELD_TYPE_STRING ?
+				" (string field only)" : "");
+
+	log_print(" ");
+	log_print("Comparison operands");
+	log_print("-------------------");
+	log_print("  numbers            - decimal, non-negative, integer only");
+	log_print("  strings            - characters quoted by ' or \"");
+	log_print("  regular expression - characters quoted by any character");
+
+	log_print(" ");
+	log_print("Combining comparison");
+	log_print("--------------------");
+	log_print("  and, &&  - logical AND");
+	log_print("  or, ||   - logical OR");
+	log_print("  ()       - grouping expressions");
+	log_print("  !        - logical NOT");
+}
+
+/*
+ * Filter components
+ */
+
+/* a comparison condition */
+struct field_filter {
+	struct field_properties *fp;
+	uint32_t flags; /* see _op_cmp[] */
+	union {
+		const char *string;
+		uint64_t number;
+		struct dm_regex *regex;
+	} v;
+};
+
+/* an expression: either comparison condition, and-clause or or-clause */
+struct filter_node {
+	uint32_t type; /* FILTER_* */
+	struct list list;
+	union {
+		struct field_filter * f; /* type is COND */
+		struct list l; /* type is AND or OR */
+	} e;
+};
+
+static struct field_filter *_create_field_filter(struct dm_report *rh,
+						 uint32_t field_num,
+						 const char *v, size_t len,
+						 uint32_t flags)
+{
+	struct field_properties *fp, *found = NULL;
+	struct field_filter *filter;
+	char *s;
+
+	list_iterate_items(fp, &rh->field_props) {
+		if (fp->field_num == field_num) {
+			found = fp;
+			break;
+		}
+	}
+
+	/* The field is neither used in display options nor sort keys. */
+	if (!found) {
+		if (!(found = _add_field(rh, field_num, FLD_HIDDEN)))
+			return NULL;
+	}
+
+	if (!(found->flags & flags & DM_REPORT_FIELD_TYPE_MASK)) {
+		log_error("dm_report: Incompatible comparison type");
+		return NULL;
+	}
+
+	/* set up filter */
+	if (!(filter = dm_pool_zalloc(rh->mem, sizeof(struct field_filter)))) {
+		log_error("dm_report: struct field_filter allocation failed");
+		return NULL;
+	}
+	filter->fp = found;
+	filter->flags = flags;
+
+	/* store comparison operand */
+	if (flags & FLD_CMP_REGEX) {
+		if (!(s = dm_malloc(len + 1))) {
+			log_error("dm_report: dm_malloc failed");
+			goto out_free_filter;
+		}
+		memcpy(s, v, len);
+		s[len] = '\0';
+
+		filter->v.regex = dm_regex_create(rh->mem,
+						  (const char **) &s, 1);
+		dm_free(s);
+		if (!filter->v.regex) {
+			log_error("dm_report: failed to create matcher");
+			goto out_free_filter;
+		}
+	} else {
+		if (!(s = dm_pool_alloc(rh->mem, len + 1))) {
+			log_error("dm_report: dm_pool_alloc failed");
+			goto out_free_filter;
+		}
+		memcpy(s, v, len);
+		s[len] = '\0';
+
+		if (flags & DM_REPORT_FIELD_TYPE_STRING) {
+				filter->v.string = s;
+		} else {
+			filter->v.number = strtoul(s, NULL, 0);
+			dm_pool_free(rh->mem, s);
+		}
+	}
+
+	return filter;
+
+  out_free_filter:
+	dm_pool_free(rh->mem, filter);
+	return NULL;
+}
+
+static struct field_filter * _filter_match(struct dm_report *rh,
+					   const char *field, size_t flen,
+					   const char *value, size_t vlen,
+					   uint32_t flags)
+{
+	uint32_t f;
+
+	if (!flen)
+		return NULL;
+
+	for (f = 0; rh->fields[f].report_fn; f++)
+		if (_is_same_field(rh->fields[f].id,
+				   field, flen, rh->field_prefix))
+			return _create_field_filter(rh, f, value, vlen, flags);
+
+	log_print("Undefined field name");
+	return NULL;
+}
+
+static struct filter_node * _alloc_filter_node(struct dm_pool *mem,
+					       uint32_t type)
+{
+	struct filter_node *n;
+
+	if (!(n = dm_pool_zalloc(mem, sizeof(struct filter_node)))) {
+		log_error("dm_report: struct filter_node allocation failed");
+		return NULL;
+	}
+
+	list_init(&n->list);
+
+	n->type = type;
+	if (!(type & FILTER_COND))
+		list_init(&n->e.l);
+	return n;
+}
+
+/*
+ * Filter parser
+ *
+ * _parse_* functions
+ *
+ *   Input:
+ *     s             - a pointer to the parsed string
+ *   Output:
+ *     next          - a pointer used for next _parse_*'s input,
+ *                     next == s if return value is NULL
+ *     return value  - a filter node pointer,
+ *                     NULL if s doesn't match
+ */
+
+/*
+ * CONDITION := FIELD_NAME OP_CMP STRING |
+ *              FIELD_NAME OP_CMP NUMBER  |
+ *              FIELD_NAME OP_REGEX REGEX
+ */
+static struct filter_node * _parse_condition(struct dm_report *rh,
+					     const char *s, const char **next)
+{
+	struct field_filter *f;
+	struct filter_node *n;
+	const char *ws, *we; /* field name */
+	const char *vs, *ve; /* value */
+	const char *last;
+	uint32_t flags;
+	char quote;
+
+	/* field name */
+	if (!(last = _tok_field_name(s, &ws, &we))) {
+		log_error("Expecting field name");
+		goto syntax_error;
+	}
+	if (!last) {
+		log_error("Missing operator after the field name");
+		goto syntax_error;
+	}
+
+	/* comparison operator */
+	if (!(flags = _tok_op_cmp(we, &last))) {
+		log_error("Unrecognized comparison operator: %s", s);
+		goto syntax_error;
+	}
+	if (!last) {
+		log_error("Missing value after operator");
+		goto syntax_error;
+	}
+
+	/* comparison value */
+	if (flags & FLD_CMP_REGEX) {
+		if (!(last = _tok_regex(last, &vs, &ve, &quote)))
+			goto syntax_error;
+	} else {
+		if (!(last = _tok_value(last, &vs, &ve, &quote)))
+			goto syntax_error;
+
+		if (quote) {
+			/* the token is strings */
+			if (flags & DM_REPORT_FIELD_TYPE_NUMBER) {
+				log_print("The operator requires number");
+				goto syntax_error;
+			}
+			flags |= DM_REPORT_FIELD_TYPE_STRING;
+		} else {
+			/* the token is number */
+			if (flags & DM_REPORT_FIELD_TYPE_STRING) {
+				log_print("The operator requires string");
+				goto syntax_error;
+			}
+			flags |= DM_REPORT_FIELD_TYPE_NUMBER;
+		}
+	}
+	*next = _skip_space(last);
+
+	/* store condition */
+	f = _filter_match(rh, ws, (size_t) (we - ws),
+			  vs, (size_t) (ve - vs), flags);
+	if (!f)
+		goto syntax_error;
+
+	if (!(n = _alloc_filter_node(rh->mem, FILTER_COND)))
+		return NULL;
+	n->e.f = f;
+	return n;
+
+  syntax_error:
+	log_error("Filter syntax error at %s", s);
+	*next = s;
+	return NULL;
+}
+
+/* EX := CONDITION | '!'? '(' EXPRESSION ')' */
+static struct filter_node * _parse_or_ex(struct dm_report *,
+					 const char *, const char **,
+					 struct filter_node *);
+
+static struct filter_node * _parse_ex(struct dm_report *rh,
+					 const char *s, const char **next)
+{
+	struct filter_node *n = NULL;
+	uint32_t t;
+	const char *tmp;
+
+	t = _tok_op_log(s, next, FILTER_NOT|FILTER_PS);
+	if (t == FILTER_NOT) {
+		/* '!' '(' EXPRESSION ')' */
+		if (!_tok_op_log(*next, &tmp, FILTER_PS)) {
+			log_error("Syntax error: '(' expected");
+			goto out_error;
+		}
+		if (!(n = _parse_or_ex(rh, tmp, next, NULL)))
+			goto out_error;
+		n->type |= FILTER_NOT;
+		if (!_tok_op_log(*next, &tmp, FILTER_PE)) {
+			log_error("Syntax error: ')' expected");
+			goto out_error;
+		}
+		*next = tmp;
+	} else if (t == FILTER_PS) {
+		/* '(' EXPRESSION ')' */
+		if (!(n = _parse_or_ex(rh, *next, &tmp, NULL)))
+			goto out_error;
+		if (!_tok_op_log(tmp, next, FILTER_PE)) {
+			log_error("Syntax error: ')' expected");
+			goto out_error;
+		}
+	} else if ((s = _skip_space(s))) {
+		/* CONDITION */
+		n = _parse_condition(rh, s, next);
+	} else {
+		n = NULL;
+		*next = s;
+	}
+
+	return n;
+
+  out_error:
+	*next = s;
+	return NULL;
+}
+
+/* AND_EXPRESSION := EX (AND_OP AND_EXPRSSION) */
+static struct filter_node * _parse_and_ex(struct dm_report *rh,
+					  const char *s, const char **next,
+					  struct filter_node *and_n)
+{
+	struct filter_node *n;
+	const char *tmp;
+
+	n = _parse_ex(rh, s, next);
+	if (!n)
+		goto out_error;
+
+	if (!_tok_op_log(*next, &tmp, FILTER_AND)) {
+		if (!and_n)
+			return n;
+		list_add(&and_n->e.l, &n->list);
+		return and_n;
+	}
+
+	if (!and_n) {
+		if (!(and_n = _alloc_filter_node(rh->mem, FILTER_AND)))
+			goto out_error;
+	}
+	list_add(&and_n->e.l, &n->list);
+
+	return _parse_and_ex(rh, tmp, next, and_n);
+
+  out_error:
+	*next = s;
+	return NULL;
+}
+
+/* OR_EXPRESSION := AND_EXPRESSION (OR_OP OR_EXPRESSION) */
+static struct filter_node * _parse_or_ex(struct dm_report *rh,
+					 const char *s, const char **next,
+					 struct filter_node *or_n)
+{
+	struct filter_node *n;
+	const char *tmp;
+
+	n = _parse_and_ex(rh, s, next, NULL);
+	if (!n)
+		goto out_error;
+
+	if (!_tok_op_log(*next, &tmp, FILTER_OR)) {
+		if (!or_n)
+			return n;
+		list_add(&or_n->e.l, &n->list);
+		return or_n;
+	}
+
+	if (!or_n) {
+		if (!(or_n = _alloc_filter_node(rh->mem, FILTER_OR)))
+			goto out_error;
+	}
+	list_add(&or_n->e.l, &n->list);
+
+	return _parse_or_ex(rh, tmp, next, or_n);
+
+  out_error:
+	*next = s;
+	return NULL;
+}
+
+
+int dm_report_set_filter(struct dm_report *rh, const char *string,
+			 uint32_t flags)
+{
+	const char *fin;
+	struct filter_node *root;
+
+	if (flags) {
+		log_error("dm_report_set_filter: flags not supported");
+		return 0;
+	}
+
+	if (rh->filter_root) {
+		log_error("dm_report_set_filter: filter already set");
+		return 0;
+	}
+
+	/* null string means no filter */
+	if (!string || !string[0])
+		return 1;
+
+	if (!(root = _alloc_filter_node(rh->mem, FILTER_OR)))
+		goto out_error;
+
+	if (!_parse_or_ex(rh, string, &fin, root) || *_skip_space(fin)) {
+		dm_pool_free(rh->mem, root);
+		goto out_error;
+	}
+
+	rh->filter_root = root;
+	return 1;
+
+  out_error:
+	_display_operators();
+	log_print(" ");
+	_display_fields(rh);
+	log_print(" ");
+	return 0;
+}
+
 struct dm_report *dm_report_init(uint32_t *report_types,
 				 const struct dm_report_object_type *types,
 				 const struct dm_report_field_type *fields,
@@ -551,6 +1203,65 @@ void dm_report_free(struct dm_report *rh
 /*
  * Create a row of data for an object
  */
+static int _cmp_field_number(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_GT:
+		return a > b;
+	case FLD_CMP_GT|FLD_CMP_EQUAL:
+		return a >= b;
+	case FLD_CMP_LT:
+		return a < b;
+	case FLD_CMP_LT|FLD_CMP_EQUAL:
+		return a <= b;
+	default:
+		log_error("Unsupported comparison type for number");
+	}
+	return 0;
+}
+
+static int _cmp_field_string(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("Unsupported comparison type for string");
+	}
+	return 0;
+}
+
+static int _cmp_field_regex(const char *s, struct dm_regex *r, uint32_t flags)
+{
+	return (dm_regex_match(r, s) >= 0) ^ ((flags & FLD_CMP_NOT) != 0);
+}
+
+static int _compare_field(struct dm_report_field *field,
+			  struct field_filter *filter)
+{
+	if (!field->sort_value) {
+		log_error("dm_report: field without value: %d",
+			  field->props->field_num);
+		return 0;
+	}
+
+	if (filter->flags & FLD_CMP_REGEX)
+		return _cmp_field_regex((const char *) field->sort_value,
+					filter->v.regex, filter->flags);
+	else if (field->props->flags & DM_REPORT_FIELD_TYPE_NUMBER)
+		return _cmp_field_number(*(const uint64_t *)field->sort_value,
+					 filter->v.number, filter->flags);
+	else /* DM_REPORT_FIELD_TYPE_STRING */
+		return _cmp_field_string((const char *) field->sort_value,
+					 filter->v.string, filter->flags);
+}
+
 static void * _report_get_field_data(struct dm_report *rh,
 			      struct field_properties *fp, void *object)
 {
@@ -562,6 +1273,51 @@ static void * _report_get_field_data(str
 	return ret + rh->fields[fp->field_num].offset;
 }
 
+static int _filter(struct filter_node *n, struct list *fields)
+{
+	int r;
+	struct filter_node *f;
+	struct dm_report_field *field;
+
+	switch (n->type & FILTER_TYPE_MASK) {
+		case FILTER_COND:
+			r = 1;
+			list_iterate_items(field, fields) {
+				if (n->e.f->fp != field->props)
+					continue;
+				if (!_compare_field(field, n->e.f))
+					r = 0;
+			}
+			break;
+		case FILTER_OR:
+			r = 0;
+			list_iterate_items(f, &n->e.l)
+				if ((r |= _filter(f, fields)))
+					break;
+			break;
+		case FILTER_AND:
+			r = 1;
+			list_iterate_items(f, &n->e.l)
+				if (!(r &= _filter(f, fields)))
+					break;
+			break;
+		default:
+			log_error("Unsupported filter type");
+			return 0;
+	}
+
+	return (n->type & FILTER_NOT) ? !r : r;
+}
+
+/* the object is given as a list of "struct field"s */
+static int _filter_object(struct dm_report *rh, struct list *fields)
+{
+	if (!rh->filter_root)
+		return 1;
+
+	return _filter(rh->filter_root, fields);
+}
+
 int dm_report_object(struct dm_report *rh, void *object)
 {
 	struct field_properties *fp;
@@ -582,24 +1338,23 @@ int dm_report_object(struct dm_report *r
 			       rh->keys_count))) {
 		log_error("dm_report_object: "
 			  "row sort value structure allocation failed");
-		return 0;
+		goto out_free_row;
 	}
 
 	list_init(&row->fields);
-	list_add(&rh->rows, &row->list);
 
 	/* For each field to be displayed, call its report_fn */
 	list_iterate_items(fp, &rh->field_props) {
 		if (!(field = dm_pool_zalloc(rh->mem, sizeof(*field)))) {
 			log_error("dm_report_object: "
 				  "struct dm_report_field allocation failed");
-			return 0;
+			goto out_free_row;
 		}
 		field->props = fp;
 
 		data = _report_get_field_data(rh, fp, object);
 		if (!data)
-			return 0;
+			goto out_free_row;
 
 		if (!rh->fields[fp->field_num].report_fn(rh, rh->mem,
 							 field, data,
@@ -607,9 +1362,20 @@ int dm_report_object(struct dm_report *r
 			log_error("dm_report_object: "
 				  "report function failed for field %s",
 				  rh->fields[fp->field_num].id);
-			return 0;
+			goto out_free_row;
 		}
 
+		list_add(&row->fields, &field->list);
+	}
+
+	/* Check filter and decide whether to display or not */
+	if (!_filter_object(rh, &row->fields))
+		goto out_free_row;
+
+	list_add(&rh->rows, &row->list);
+
+	/* For the object to be displayed, udpate width and record sort value */
+	list_iterate_items(field, &row->fields) {
 		if ((strlen(field->report_string) > field->props->width))
 			field->props->width = strlen(field->report_string);
 
@@ -617,13 +1383,16 @@ int dm_report_object(struct dm_report *r
 		    (field->props->flags & FLD_SORT_KEY)) {
 			(*row->sort_fields)[field->props->sort_posn] = field;
 		}
-		list_add(&row->fields, &field->list);
 	}
 
 	if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
 		return dm_report_output(rh);
 
 	return 1;
+
+  out_free_row:
+	dm_pool_free(rh->mem, row);
+	return 0;
 }
 
 /*
--
dm-devel mailing list
dm-devel@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/dm-devel

[Index of Archives]     [DM Crypt]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Packaging]     [Fedora SELinux]     [Yosemite Discussion]     [KDE Users]     [Fedora Docs]

  Powered by Linux