1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-02-24 17:57:48 +03:00

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.
This commit is contained in:
Peter Rajnoha 2014-05-29 09:37:54 +02:00
parent 4118dd8da3
commit 2c3e84a68d

View File

@ -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 := <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
*/