mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
report: select: add dm_report_init_with_selection to libdm
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 The dm_report_init_with_selection is the same as dm_report_init but it contains an additional argument to set the selection in the form of a string that contains field names to check against and selection operators. The selection string is parsend and a selection tree is composed for use in the checks against individual fields when the report is processed. The parsed selection tree is stored in dm_report structure as "selection_root".
This commit is contained in:
parent
2c3e84a68d
commit
0103738ef5
@ -1641,7 +1641,18 @@ struct dm_report *dm_report_init(uint32_t *report_types,
|
||||
uint32_t output_flags,
|
||||
const char *sort_keys,
|
||||
void *private_data);
|
||||
struct dm_report *dm_report_init_with_selection(uint32_t *report_types,
|
||||
const struct dm_report_object_type *types,
|
||||
const struct dm_report_field_type *fields,
|
||||
const char *output_fields,
|
||||
const char *output_separator,
|
||||
uint32_t output_flags,
|
||||
const char *sort_keys,
|
||||
const char *selection,
|
||||
void *private_data);
|
||||
int dm_report_object(struct dm_report *rh, void *object);
|
||||
int dm_report_set_output_selection(struct dm_report *rh, uint32_t *report_types,
|
||||
const char *selection);
|
||||
int dm_report_output(struct dm_report *rh);
|
||||
void dm_report_free(struct dm_report *rh);
|
||||
|
||||
|
@ -48,6 +48,8 @@ struct dm_report {
|
||||
|
||||
/* To store caller private data */
|
||||
void *private;
|
||||
|
||||
struct selection_node *selection_root;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -496,6 +498,23 @@ static int _add_all_fields(struct dm_report *rh, uint32_t type)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _get_field(struct dm_report *rh, const char *field, size_t flen, uint32_t *f_ret)
|
||||
{
|
||||
uint32_t f;
|
||||
|
||||
if (!flen)
|
||||
return 0;
|
||||
|
||||
for (f = 0; rh->fields[f].report_fn; f++) {
|
||||
if (_is_same_field(rh->fields[f].id, field, flen, rh->field_prefix)) {
|
||||
*f_ret = f;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _field_match(struct dm_report *rh, const char *field, size_t flen,
|
||||
unsigned report_type_only)
|
||||
{
|
||||
@ -504,9 +523,7 @@ static int _field_match(struct dm_report *rh, const char *field, size_t flen,
|
||||
if (!flen)
|
||||
return 0;
|
||||
|
||||
for (f = 0; rh->fields[f].report_fn; f++)
|
||||
if (_is_same_field(rh->fields[f].id, field, flen,
|
||||
rh->field_prefix)) {
|
||||
if ((_get_field(rh, field, flen, &f))) {
|
||||
if (report_type_only) {
|
||||
rh->report_types |= rh->fields[f].type;
|
||||
return 1;
|
||||
@ -1096,6 +1113,13 @@ static const char *_tok_value(const struct dm_report_field_type *ft,
|
||||
*flags |= DM_REPORT_FIELD_TYPE_NUMBER;
|
||||
} else {
|
||||
s = tmp;
|
||||
/*
|
||||
* If size unit is not defined in the selection
|
||||
* use use 'm' (1 MiB) by default. This is the
|
||||
* same behaviour as seen in lvcreate -L <size>.
|
||||
*/
|
||||
if (!*factor)
|
||||
*factor = 1024*1024;
|
||||
*flags |= DM_REPORT_FIELD_TYPE_SIZE;
|
||||
}
|
||||
}
|
||||
@ -1128,7 +1152,399 @@ static const char *_tok_field_name(const char *s,
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct field_selection *_create_field_selection(struct dm_report *rh,
|
||||
uint32_t field_num,
|
||||
const char *v,
|
||||
size_t len,
|
||||
uint32_t flags,
|
||||
void *custom)
|
||||
{
|
||||
static const char *_out_of_range_msg = "Field selection value %s out of supported range for field %s.";
|
||||
struct field_properties *fp, *found = NULL;
|
||||
struct field_selection *fs;
|
||||
const char *field_id;
|
||||
uint64_t factor;
|
||||
char *s;
|
||||
|
||||
dm_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;
|
||||
rh->report_types |= rh->fields[field_num].type;
|
||||
}
|
||||
|
||||
field_id = rh->fields[found->field_num].id;
|
||||
|
||||
if (!(found->flags & flags & DM_REPORT_FIELD_TYPE_MASK)) {
|
||||
log_error("dm_report: incompatible comparison "
|
||||
"type for selection field %s", field_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set up selection */
|
||||
if (!(fs = dm_pool_zalloc(rh->mem, sizeof(struct field_selection)))) {
|
||||
log_error("dm_report: struct field_selection "
|
||||
"allocation failed for selection field %s", field_id);
|
||||
return NULL;
|
||||
}
|
||||
fs->fp = found;
|
||||
fs->flags = flags;
|
||||
|
||||
/* store comparison operand */
|
||||
if (flags & FLD_CMP_REGEX) {
|
||||
/* REGEX */
|
||||
if (!(s = dm_malloc(len + 1))) {
|
||||
log_error("dm_report: dm_malloc failed to store "
|
||||
"regex value for selection field %s", field_id);
|
||||
goto error;
|
||||
}
|
||||
memcpy(s, v, len);
|
||||
s[len] = '\0';
|
||||
|
||||
fs->v.r = dm_regex_create(rh->mem, (const char **) &s, 1);
|
||||
dm_free(s);
|
||||
if (!fs->v.r) {
|
||||
log_error("dm_report: failed to create regex "
|
||||
"matcher for selection field %s", field_id);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
/* STRING, NUMBER or SIZE */
|
||||
if (!(s = dm_pool_alloc(rh->mem, len + 1))) {
|
||||
log_error("dm_report: dm_pool_alloc failed to store "
|
||||
"value for selection field %s", field_id);
|
||||
goto error;
|
||||
}
|
||||
memcpy(s, v, len);
|
||||
s[len] = '\0';
|
||||
|
||||
switch (flags & DM_REPORT_FIELD_TYPE_MASK) {
|
||||
case DM_REPORT_FIELD_TYPE_STRING:
|
||||
fs->v.s = s;
|
||||
break;
|
||||
case DM_REPORT_FIELD_TYPE_NUMBER:
|
||||
if (((fs->v.i = strtoull(s, NULL, 10)) == ULLONG_MAX) &&
|
||||
(errno == ERANGE)) {
|
||||
log_error(_out_of_range_msg, s, field_id);
|
||||
goto error;
|
||||
}
|
||||
dm_pool_free(rh->mem, s);
|
||||
break;
|
||||
case DM_REPORT_FIELD_TYPE_SIZE:
|
||||
fs->v.d = strtod(s, NULL);
|
||||
if (errno == ERANGE) {
|
||||
log_error(_out_of_range_msg, s, field_id);
|
||||
goto error;
|
||||
}
|
||||
if (custom && (factor = *((uint64_t *)custom)))
|
||||
fs->v.d *= factor;
|
||||
fs->v.d /= 512; /* store size in sectors! */
|
||||
dm_pool_free(rh->mem, s);
|
||||
break;
|
||||
default:
|
||||
log_error(INTERNAL_ERROR "_create_field_selection: "
|
||||
"unknown type of selection field %s", field_id);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return fs;
|
||||
error:
|
||||
dm_pool_free(rh->mem, fs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct selection_node *_alloc_selection_node(struct dm_pool *mem, uint32_t type)
|
||||
{
|
||||
struct selection_node *sn;
|
||||
|
||||
if (!(sn = dm_pool_zalloc(mem, sizeof(struct selection_node)))) {
|
||||
log_error("dm_report: struct selection_node allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dm_list_init(&sn->list);
|
||||
sn->type = type;
|
||||
if (!(type & SEL_ITEM))
|
||||
dm_list_init(&sn->selection.set);
|
||||
|
||||
return sn;
|
||||
}
|
||||
|
||||
static char _sel_syntax_error_at_msg[] = "Selection syntax error at '%s'.";
|
||||
|
||||
/*
|
||||
* Selection 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* SELECTION := FIELD_NAME OP_CMP STRING |
|
||||
* FIELD_NAME OP_CMP NUMBER |
|
||||
* FIELD_NAME OP_REGEX REGEX
|
||||
*/
|
||||
static struct selection_node *_parse_selection(struct dm_report *rh,
|
||||
const char *s,
|
||||
const char **next)
|
||||
{
|
||||
struct field_selection *fs;
|
||||
struct selection_node *sn;
|
||||
const char *ws, *we; /* field name */
|
||||
const char *vs, *ve; /* value */
|
||||
const char *last;
|
||||
uint32_t flags, field_num;
|
||||
const struct dm_report_field_type *ft;
|
||||
uint64_t factor;
|
||||
void *custom;
|
||||
char *tmp;
|
||||
char c;
|
||||
|
||||
/* field name */
|
||||
if (!(last = _tok_field_name(s, &ws, &we))) {
|
||||
log_error("Expecting field name");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* check if the field with given name exists */
|
||||
if (!_get_field(rh, ws, (size_t) (we - ws), &field_num)) {
|
||||
c = we[0];
|
||||
tmp = (char *) we;
|
||||
tmp[0] = '\0';
|
||||
log_error("Unrecognised selection field: %s", ws);
|
||||
tmp[0] = c;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
ft = &rh->fields[field_num];
|
||||
|
||||
/* comparison operator */
|
||||
if (!(flags = _tok_op_cmp(we, &last))) {
|
||||
log_error("Unrecognised comparison operator: %s", we);
|
||||
goto bad;
|
||||
}
|
||||
if (!last) {
|
||||
log_error("Missing value after operator");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* some operators can compare only numeric fields */
|
||||
if ((flags & FLD_CMP_NUMBER) &&
|
||||
(ft->flags != DM_REPORT_FIELD_TYPE_NUMBER) &&
|
||||
(ft->flags != DM_REPORT_FIELD_TYPE_SIZE)) {
|
||||
log_error("Operator can be used only with numeric or size fields: %s", ws);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* comparison value */
|
||||
if (flags & FLD_CMP_REGEX) {
|
||||
if (!(last = _tok_value_regex(ft, last, &vs, &ve, &flags)))
|
||||
goto_bad;
|
||||
} else {
|
||||
if (ft->flags == DM_REPORT_FIELD_TYPE_SIZE)
|
||||
custom = &factor;
|
||||
else
|
||||
custom = NULL;
|
||||
if (!(last = _tok_value(ft, last, &vs, &ve, &flags, rh->mem, custom)))
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
*next = _skip_space(last);
|
||||
|
||||
/* create selection */
|
||||
if (!(fs = _create_field_selection(rh, field_num, vs, (size_t) (ve - vs), flags, custom)))
|
||||
return_NULL;
|
||||
|
||||
/* create selection node */
|
||||
if (!(sn = _alloc_selection_node(rh->mem, SEL_ITEM)))
|
||||
return_NULL;
|
||||
|
||||
/* add selection to selection node */
|
||||
sn->selection.item = fs;
|
||||
|
||||
return sn;
|
||||
bad:
|
||||
log_error(_sel_syntax_error_at_msg, s);
|
||||
*next = s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct selection_node *_parse_or_ex(struct dm_report *rh,
|
||||
const char *s,
|
||||
const char **next,
|
||||
struct selection_node *or_sn);
|
||||
|
||||
static struct selection_node *_parse_ex(struct dm_report *rh,
|
||||
const char *s,
|
||||
const char **next)
|
||||
{
|
||||
static const char _ps_expected_msg[] = "Syntax error: left parenthesis expected at \'%s\'";
|
||||
static const char _pe_expected_msg[] = "Syntax error: right parenthesis expected at \'%s\'";
|
||||
struct selection_node *sn = NULL;
|
||||
uint32_t t;
|
||||
const char *tmp;
|
||||
|
||||
t = _tok_op_log(s, next, SEL_MODIFIER_NOT | SEL_PRECEDENCE_PS);
|
||||
if (t == SEL_MODIFIER_NOT) {
|
||||
/* '!' '(' EXPRESSION ')' */
|
||||
if (!_tok_op_log(*next, &tmp, SEL_PRECEDENCE_PS)) {
|
||||
log_error(_ps_expected_msg, *next);
|
||||
goto error;
|
||||
}
|
||||
if (!(sn = _parse_or_ex(rh, tmp, next, NULL)))
|
||||
goto error;
|
||||
sn->type |= SEL_MODIFIER_NOT;
|
||||
if (!_tok_op_log(*next, &tmp, SEL_PRECEDENCE_PE)) {
|
||||
log_error(_pe_expected_msg, *next);
|
||||
goto error;
|
||||
}
|
||||
*next = tmp;
|
||||
} else if (t == SEL_PRECEDENCE_PS) {
|
||||
/* '(' EXPRESSION ')' */
|
||||
if (!(sn = _parse_or_ex(rh, *next, &tmp, NULL)))
|
||||
goto error;
|
||||
if (!_tok_op_log(tmp, next, SEL_PRECEDENCE_PE)) {
|
||||
log_error(_pe_expected_msg, *next);
|
||||
goto error;
|
||||
}
|
||||
} else if ((s = _skip_space(s))) {
|
||||
/* SELECTION */
|
||||
sn = _parse_selection(rh, s, next);
|
||||
} else {
|
||||
sn = NULL;
|
||||
*next = s;
|
||||
}
|
||||
|
||||
return sn;
|
||||
error:
|
||||
*next = s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* AND_EXPRESSION := EX (AND_OP AND_EXPRSSION) */
|
||||
static struct selection_node *_parse_and_ex(struct dm_report *rh,
|
||||
const char *s,
|
||||
const char **next,
|
||||
struct selection_node *and_sn)
|
||||
{
|
||||
struct selection_node *n;
|
||||
const char *tmp;
|
||||
|
||||
n = _parse_ex(rh, s, next);
|
||||
if (!n)
|
||||
goto error;
|
||||
|
||||
if (!_tok_op_log(*next, &tmp, SEL_AND)) {
|
||||
if (!and_sn)
|
||||
return n;
|
||||
dm_list_add(&and_sn->selection.set, &n->list);
|
||||
return and_sn;
|
||||
}
|
||||
|
||||
if (!and_sn) {
|
||||
if (!(and_sn = _alloc_selection_node(rh->mem, SEL_AND)))
|
||||
goto error;
|
||||
}
|
||||
dm_list_add(&and_sn->selection.set, &n->list);
|
||||
|
||||
return _parse_and_ex(rh, tmp, next, and_sn);
|
||||
error:
|
||||
*next = s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* OR_EXPRESSION := AND_EXPRESSION (OR_OP OR_EXPRESSION) */
|
||||
static struct selection_node *_parse_or_ex(struct dm_report *rh,
|
||||
const char *s,
|
||||
const char **next,
|
||||
struct selection_node *or_sn)
|
||||
{
|
||||
struct selection_node *n;
|
||||
const char *tmp;
|
||||
|
||||
n = _parse_and_ex(rh, s, next, NULL);
|
||||
if (!n)
|
||||
goto error;
|
||||
|
||||
if (!_tok_op_log(*next, &tmp, SEL_OR)) {
|
||||
if (!or_sn)
|
||||
return n;
|
||||
dm_list_add(&or_sn->selection.set, &n->list);
|
||||
return or_sn;
|
||||
}
|
||||
|
||||
if (!or_sn) {
|
||||
if (!(or_sn = _alloc_selection_node(rh->mem, SEL_OR)))
|
||||
goto error;
|
||||
}
|
||||
dm_list_add(&or_sn->selection.set, &n->list);
|
||||
|
||||
return _parse_or_ex(rh, tmp, next, or_sn);
|
||||
error:
|
||||
*next = s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dm_report *dm_report_init_with_selection(uint32_t *report_types,
|
||||
const struct dm_report_object_type *types,
|
||||
const struct dm_report_field_type *fields,
|
||||
const char *output_fields,
|
||||
const char *output_separator,
|
||||
uint32_t output_flags,
|
||||
const char *sort_keys,
|
||||
const char *selection,
|
||||
void *private_data)
|
||||
{
|
||||
struct dm_report *rh;
|
||||
struct selection_node *root = NULL;
|
||||
const char *fin, *next;
|
||||
|
||||
if (!(rh = dm_report_init(report_types, types, fields, output_fields,
|
||||
output_separator, output_flags, sort_keys, private_data)))
|
||||
return NULL;
|
||||
|
||||
if (!selection || !selection[0]) {
|
||||
rh->selection_root = NULL;
|
||||
return rh;
|
||||
}
|
||||
|
||||
if (!(root = _alloc_selection_node(rh->mem, SEL_OR)))
|
||||
return_0;
|
||||
|
||||
if (!_parse_or_ex(rh, selection, &fin, root))
|
||||
goto error;
|
||||
|
||||
next = _skip_space(fin);
|
||||
if (*next) {
|
||||
log_error("Expecting logical operator");
|
||||
log_error(_sel_syntax_error_at_msg, next);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (report_types)
|
||||
*report_types = rh->report_types;
|
||||
|
||||
rh->selection_root = root;
|
||||
return rh;
|
||||
error:
|
||||
dm_report_free(rh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print row of headings
|
||||
|
Loading…
Reference in New Issue
Block a user