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 } }; @@ -853,6 +853,283 @@ int dm_report_object(struct dm_report *rh, void *object) return 1; } +/* + * Selection parsing + */ + +/* + * Other tokens (FIELD, VALUE, STRING, NUMBER, REGEX) + * FIELD := + * VALUE := NUMBER | STRING + * REGEX := + * NUMBER := (because sort_value is unsigned) + * STRING := + */ + +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 */