diff options
author | Peter Rajnoha <prajnoha@redhat.com> | 2014-05-29 09:37:54 +0200 |
---|---|---|
committer | Peter Rajnoha <prajnoha@redhat.com> | 2014-06-17 16:27:20 +0200 |
commit | 2c3e84a68d1f991bfe47637017723b7cf0435dff (patch) | |
tree | e24b54f358288cf0b55ac7b1ffa8e321775541ff | |
parent | 4118dd8da3d2603bafffb024739e5033e9f6543d (diff) | |
download | lvm2-2c3e84a68d1f991bfe47637017723b7cf0435dff.tar.gz |
report: select: add supporting infrastucture for token parsing in report selections
This is rebased and edited version of the original design and
patch proposed by Jun'ichi Nomura:
http://www.redhat.com/archives/dm-devel/2007-April/msg00025.html
Add support for parsing numbers, strings (quoted or unquoted), regexes
and operators amogst these operands in selection condition supplied.
-rw-r--r-- | libdm/libdm-report.c | 281 |
1 files changed, 279 insertions, 2 deletions
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c index ae1c70176..882bf3ecc 100644 --- a/libdm/libdm-report.c +++ b/libdm/libdm-report.c @@ -102,8 +102,8 @@ static struct op_def _op_cmp[] = { { "!=", FLD_CMP_NOT|FLD_CMP_EQUAL, "Not equal to." }, { ">=", FLD_CMP_NUMBER|FLD_CMP_GT|FLD_CMP_EQUAL, "Greater than or equal to." }, { ">", FLD_CMP_NUMBER|FLD_CMP_GT, "Greater than" }, - { "<=", FLD_CMP_NUMBER|FLD_CMP_LT|FLD_CMP_EQUAL, "Lesser than or equal to." }, - { "<", FLD_CMP_NUMBER|FLD_CMP_LT, "Lesser than." }, + { "<=", FLD_CMP_NUMBER|FLD_CMP_LT|FLD_CMP_EQUAL, "Less than or equal to." }, + { "<", FLD_CMP_NUMBER|FLD_CMP_LT, "Less than." }, { NULL, 0, NULL } }; @@ -854,6 +854,283 @@ int dm_report_object(struct dm_report *rh, void *object) } /* + * Selection parsing + */ + +/* + * Other tokens (FIELD, VALUE, STRING, NUMBER, REGEX) + * FIELD := <strings of alphabet, number and '_'> + * VALUE := NUMBER | STRING + * REGEX := <strings quoted by '"', '\'', '(', '{', '[' or unquoted> + * NUMBER := <strings of [0-9]> (because sort_value is unsigned) + * STRING := <strings quoted by '"', '\'' or unquoted> + */ + +static const char * _skip_space(const char *s) +{ + while (*s && isspace(*s)) + s++; + return s; +} + +static int _tok_op(struct op_def *t, const char *s, const char **end, + uint32_t expect) +{ + size_t len; + + s = _skip_space(s); + + for (; t->string; t++) { + if (expect && !(t->flags & expect)) + continue; + + len = strlen(t->string); + if (!strncmp(s, t->string, len)) { + if (end) + *end = s + len; + return t->flags; + } + } + + if (end) + *end = s; + return 0; +} + +static int _tok_op_log(const char *s, const char **end, uint32_t expect) +{ + return _tok_op(_op_log, s, end, expect); +} + +static int _tok_op_cmp(const char *s, const char **end) +{ + return _tok_op(_op_cmp, s, end, 0); +} + + /* + * + * 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 or + * NULL if 's' doesn't match with token type + * (the parsing should be terminated) + */ +static const char *_tok_value_number(const char *s, + const char **begin, const char **end) + +{ + int is_float = 0; + + *begin = s; + while (*s && ((!is_float && *s=='.' && (is_float=1)) || isdigit(*s))) + s++; + *end = s; + + if (*begin == *end) + return NULL; + + return s; +} + +/* + * Input: + * s - a pointer to the parsed string + * endchar - terminating character + * end_op_flags - terminating operator flags (see _op_log) + * (if endchar is non-zero then endflags is ignored) + * Output: + * begin - a pointer to the beginning of the token + * end - a pointer to the end of the token + 1 + * end_op_flag_hit - the flag from endflags hit during parsing + * return value - a starting point of the next parsing + */ +static const char *_tok_value_string(const char *s, + const char **begin, const char **end, + const char endchar, uint32_t end_op_flags, + uint32_t *end_op_flag_hit) +{ + uint32_t flag_hit = 0; + + *begin = s; + + /* + * If endchar is defined, scan the string till + * the endchar or the end of string is hit. + * This is in case the string is quoted and we + * know exact character that is the stopper. + */ + if (endchar) { + while (*s && *s != endchar) + s++; + if (*s != endchar) { + log_error("Missing end quote."); + return NULL; + } + *end = s; + s++; + } else { + /* + * If endchar is not defined then endchar is/are the + * operator/s as defined by 'endflags' arg or space char. + * This is in case the string is not quoted and + * we don't know which character is the exact stopper. + */ + while (*s) { + if ((flag_hit = _tok_op(_op_log, s, NULL, end_op_flags)) || *s == ' ') + break; + s++; + } + *end = s; + /* + * If we hit one of the strings as defined by 'endflags' + * and if 'endflag_hit' arg is provided, save the exact + * string flag that was hit. + */ + if (end_op_flag_hit) + *end_op_flag_hit = flag_hit; + } + + return s; +} + +/* + * Input: + * ft - field type for which the value is parsed + * 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 + * flags - parsing flags + */ +static const char *_tok_value_regex(const struct dm_report_field_type *ft, + const char *s, const char **begin, + const char **end, uint32_t *flags) +{ + char c; + + s = _skip_space(s); + + if (!*s) { + log_error("Regular expression expected for selection field %s", ft->id); + return NULL; + } + + switch (*s) { + case '(': c = ')'; break; + case '{': c = '}'; break; + case '[': c = ']'; break; + case '"': + case '\'': c = *s; break; + default: c = 0; + } + + if (!(s = _tok_value_string(c ? s + 1 : s, begin, end, c, SEL_AND | SEL_OR | SEL_PRECEDENCE_PE, NULL))) { + log_error("Failed to parse regex value for selection field %s.", ft->id); + return NULL; + } + + *flags |= DM_REPORT_FIELD_TYPE_STRING; + return s; +} + +/* + * Input: + * ft - field type for which the value is parsed + * s - a pointer to the parsed string + * mem - memory pool to allocate from + * Output: + * begin - a pointer to the beginning of the token + * end - a pointer to the end of the token + 1 + * flags - parsing flags + * custom - custom data specific to token type + * (e.g. size unit factor) + */ +static const char *_tok_value(const struct dm_report_field_type *ft, + const char *s, const char **begin, + const char **end, uint32_t *flags, + struct dm_pool *mem, void *custom) +{ + int expected_type = ft->flags & DM_REPORT_FIELD_TYPE_MASK; + uint64_t *factor; + const char *tmp; + char c = 0; + + s = _skip_space(s); + + switch (expected_type) { + + case DM_REPORT_FIELD_TYPE_STRING: + if (*s == '"' || *s == '\'') { + c = *s; + s++; + } + if (!(s = _tok_value_string(s, begin, end, c, SEL_AND | SEL_OR | SEL_PRECEDENCE_PE, NULL))) { + log_error("Failed to parse string value " + "for selection field %s.", ft->id); + return NULL; + } + *flags |= DM_REPORT_FIELD_TYPE_STRING; + break; + + case DM_REPORT_FIELD_TYPE_NUMBER: + case DM_REPORT_FIELD_TYPE_SIZE: + if (!(s = _tok_value_number(s, begin, end))) { + log_error("Failed to parse numeric value " + "for selection field %s.", ft->id); + return NULL; + } + factor = (uint64_t *) custom; + *factor = dm_units_to_factor(s, &c, 0, &tmp); + + if (expected_type == DM_REPORT_FIELD_TYPE_NUMBER) { + if (*factor) { + log_error("Found size unit specifier but " + "only numeric value expected for " + "selection field %s.",ft->id); + return NULL; + } + *flags |= DM_REPORT_FIELD_TYPE_NUMBER; + } else { + s = tmp; + *flags |= DM_REPORT_FIELD_TYPE_SIZE; + } + } + + return s; +} + +/* + * 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 + */ +static const char *_tok_field_name(const char *s, + const char **begin, const char **end) +{ + char c; + s = _skip_space(s); + + *begin = s; + while ((c = *s) && + (isalnum(c) || c == '_' || c == '-')) + s++; + *end = s; + + if (*begin == *end) + return NULL; + + return s; +} + + + +/* * Print row of headings */ static int _report_headings(struct dm_report *rh) |