summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Rajnoha <prajnoha@redhat.com>2014-05-29 09:37:54 +0200
committerPeter Rajnoha <prajnoha@redhat.com>2014-06-17 16:27:20 +0200
commit2c3e84a68d1f991bfe47637017723b7cf0435dff (patch)
treee24b54f358288cf0b55ac7b1ffa8e321775541ff
parent4118dd8da3d2603bafffb024739e5033e9f6543d (diff)
downloadlvm2-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.c281
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)