From c61e14d3a8412cd50d98aab604e607692c844c8a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 14 Apr 2024 21:33:30 +0200 Subject: Adding upstream version 2.40. Signed-off-by: Daniel Baumann --- libsmartcols/src/filter.c | 520 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 libsmartcols/src/filter.c (limited to 'libsmartcols/src/filter.c') diff --git a/libsmartcols/src/filter.c b/libsmartcols/src/filter.c new file mode 100644 index 0000000..dccf05c --- /dev/null +++ b/libsmartcols/src/filter.c @@ -0,0 +1,520 @@ +/* + * filter.c - functions for lines filtering + * + * Copyright (C) 2023 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: filter + * @title: Filters and counters + * @short_description: defines lines filter and counter + * + * An API to define and use filter and counters. + */ + +#include +#include +#include +#include + +#include "smartcolsP.h" + +#include "filter-parser.h" +#include "filter-scanner.h" + +/** + * scols_new_filter: + * @str: filter expression or NULL + * + * Allocated and optionally parses a new filter. + * + * Returns: new filter instance or NULL in case of error. + * + * Since: 2.40 + */ +struct libscols_filter *scols_new_filter(const char *str) +{ + struct libscols_filter *fltr = calloc(1, sizeof(*fltr)); + + if (!fltr) + return NULL; + + DBG(FLTR, ul_debugobj(fltr, "alloc")); + fltr->refcount = 1; + INIT_LIST_HEAD(&fltr->params); + INIT_LIST_HEAD(&fltr->counters); + + if (str && scols_filter_parse_string(fltr, str) != 0) { + scols_unref_filter(fltr); + return NULL; + } + + return fltr; +} + +/** + * scols_ref_filter: + * @fltr: filter instance + * + * Increment filter reference counter. + * + * Since: 2.40 + */ +void scols_ref_filter(struct libscols_filter *fltr) +{ + if (fltr) + fltr->refcount++; +} + +static void reset_filter(struct libscols_filter *fltr) +{ + if (!fltr) + return; + filter_unref_node(fltr->root); + fltr->root = NULL; + + if (fltr->src) + fclose(fltr->src); + fltr->src = NULL; + + free(fltr->errmsg); + fltr->errmsg = NULL; +} + +static void remove_counters(struct libscols_filter *fltr) +{ + if (!fltr) + return; + + DBG(FLTR, ul_debugobj(fltr, "remove all counters")); + while (!list_empty(&fltr->counters)) { + struct libscols_counter *ct = list_entry(fltr->counters.next, + struct libscols_counter, counters); + + filter_unref_node((struct filter_node *) ct->param); + list_del_init(&ct->counters); + free(ct->name); + free(ct); + } +} + +/** + * scols_unref_filter: + * @fltr: filter instance + * + * Deincrements reference counter, unallocates the filter for the last + * reference. + * + * Since: 2.40 + */ +void scols_unref_filter(struct libscols_filter *fltr) +{ + if (fltr && --fltr->refcount <= 0) { + DBG(FLTR, ul_debugobj(fltr, "dealloc")); + reset_filter(fltr); + remove_counters(fltr); + free(fltr); + } +} + +/* This is generic allocater for a new node, always use the node type specific + * functions (e.g. filter_new_param() */ +struct filter_node *__filter_new_node(enum filter_ntype type, size_t sz) +{ + struct filter_node *n = calloc(1, sz); + + if (!n) + return NULL; + + n->type = type; + n->refcount = 1; + return n; +} + +void filter_unref_node(struct filter_node *n) +{ + if (!n || --n->refcount > 0) + return; + + switch (n->type) { + case F_NODE_EXPR: + filter_free_expr((struct filter_expr *) n); + break; + case F_NODE_PARAM: + filter_free_param((struct filter_param *) n); + break; + } +} + +void filter_ref_node(struct filter_node *n) +{ + if (n) + n->refcount++; +} + +void filter_dump_node(struct ul_jsonwrt *json, struct filter_node *n) +{ + if (!n) + return; + + switch (n->type) { + case F_NODE_EXPR: + filter_dump_expr(json, (struct filter_expr *) n); + break; + case F_NODE_PARAM: + filter_dump_param(json, (struct filter_param *) n); + break; + } +} + +/** + * scols_filter_parse_string: + * @fltr: filter instance + * @str: string with filter expression + * + * Parses filter, see scols_filter_get_errmsg() for errors. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_filter_parse_string(struct libscols_filter *fltr, const char *str) +{ + yyscan_t sc; + int rc; + + reset_filter(fltr); + + if (!str || !*str) + return 0; /* empty filter is not error */ + + fltr->src = fmemopen((void *) str, strlen(str), "r"); + if (!fltr->src) + return -errno; + + yylex_init(&sc); + yyset_in(fltr->src, sc); + + rc = yyparse(sc, fltr); + yylex_destroy(sc); + + fclose(fltr->src); + fltr->src = NULL; + + ON_DBG(FLTR, scols_dump_filter(fltr, stderr)); + + return rc; +} + +/** + * scols_dump_filter: + * @fltr: filter instance + * @out: output stream + * + * Dumps internal filter nodes in JSON format. This function is mostly designed + * for debugging purpose. The fileds in the output are subject to change. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_dump_filter(struct libscols_filter *fltr, FILE *out) +{ + struct ul_jsonwrt json; + + if (!fltr || !out) + return -EINVAL; + + ul_jsonwrt_init(&json, out, 0); + ul_jsonwrt_root_open(&json); + + filter_dump_node(&json, fltr->root); + ul_jsonwrt_root_close(&json); + return 0; +} + +/** + * scols_filter_get_errmsg: + * @fltr: filter instance + * + * Returns: string with parse-error message of NULL (if no error) + * + * Since: 2.40 + */ +const char *scols_filter_get_errmsg(struct libscols_filter *fltr) +{ + return fltr ? fltr->errmsg : NULL; +} + +int filter_eval_node(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_node *n, int *status) +{ + switch (n->type) { + case F_NODE_PARAM: + return filter_eval_param(fltr, ln, (struct filter_param *) n, status); + case F_NODE_EXPR: + return filter_eval_expr(fltr, ln, (struct filter_expr *) n, status); + default: + break; + } + return -EINVAL; +} + +/** + * scols_line_apply_filter: + * @ln: apply filter to the line + * @fltr: filter instance + * @status: return 1 or 0 as result of the expression + * + * Applies filter (and also counters assisiated with the filter). + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_line_apply_filter(struct libscols_line *ln, + struct libscols_filter *fltr, int *status) +{ + int rc, res = 0; + struct libscols_iter itr; + struct filter_param *prm = NULL; + + if (!ln || !fltr) + return -EINVAL; + + /* reset column data and types stored in the filter */ + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (filter_next_param(fltr, &itr, &prm) == 0) { + filter_param_reset_holder(prm); + } + + if (fltr->root) + rc = filter_eval_node(fltr, ln, fltr->root, &res); + else + rc = 0, res = 1; /* empty filter matches all lines */ + + if (rc == 0) { + struct libscols_counter *ct = NULL; + + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_filter_next_counter(fltr, &itr, &ct) == 0) { + if ((ct->neg && res == 0) || res == 1) + filter_count_param(fltr, ln, ct); + } + } + + if (status) + *status = res; + DBG(FLTR, ul_debugobj(fltr, "filter done [rc=%d, status=%d]", rc, res)); + return rc; +} + +/** + * scols_filter_set_filler_cb: + * @fltr: filter instance + * @cb: application defined callback + * @userdata: pointer to private callback data + * + * The application can apply filter for empty lines to avoid filling the table + * with unnecessary data (for example if the line will be later removed from + * the table due to filter). + * + * This callback is used by filter to ask application to fill to the line data + * which are necessary to evaluate the filter expression. The callback + * arguments are filter, column number and userdata. + * + * + * + * ln = scols_table_new_line(tab, NULL); + * + * scols_filter_set_filler_cb(filter, my_filler, NULL); + * + * scols_line_apply_filter(line, filter, &status); + * if (status == 0) + * scols_table_remove_line(tab, line); + * else for (i = 0; i < ncolumns; i++) { + * if (scols_line_is_filled(line, i)) + * continue; + * my_filler(NULL, ln, i, NULL); + * } + * + * + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_filter_set_filler_cb(struct libscols_filter *fltr, + int (*cb)(struct libscols_filter *, + struct libscols_line *, size_t, void *), + void *userdata) +{ + if (!fltr) + return -EINVAL; + fltr->filler_cb = cb; + fltr->filler_data = userdata; + + return 0; +} + +/** + * scols_filter_new_counter: + * @fltr: filter instance + * + * Alocates a new counter instance into the filter. + * + * Returns: new counter or NULL in case of an error. + * + * Since: 2.40 + */ +struct libscols_counter *scols_filter_new_counter(struct libscols_filter *fltr) +{ + struct libscols_counter *ct; + + if (!fltr) + return NULL; + + ct = calloc(1, sizeof(*ct)); + if (!ct) + return NULL; + + DBG(FLTR, ul_debugobj(fltr, "alloc counter")); + + ct->filter = fltr; /* don't use ref.counting here */ + INIT_LIST_HEAD(&ct->counters); + list_add_tail(&ct->counters, &fltr->counters); + + + return ct; +} + +/** + * scols_counter_set_name: + * @ct: counter instance + * @name: something for humans + * + * The name is not use by library, it's just description usable for application + * when prints results from countes. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_name(struct libscols_counter *ct, const char *name) +{ + if (!ct) + return -EINVAL; + return strdup_to_struct_member(ct, name, name); +} + +/** + * scols_counter_set_param: + * @ct: counter instance + * @name: holder (column) name + * + * Assigns a counter to the column. The name is used in the same way as names + * in the filter expression. This is usable for counter that calcuate with data + * from table cells (e.g. max, sum, etc.) + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_param(struct libscols_counter *ct, const char *name) +{ + if (!ct) + return -EINVAL; + + if (ct->param) { + filter_unref_node((struct filter_node *) ct->param); + ct->param = NULL; + } + if (name) { + ct->param = (struct filter_param *) + filter_new_param(ct->filter, SCOLS_DATA_U64, + F_HOLDER_COLUMN, (void *) name); + if (!ct->param) + return -ENOMEM; + } + return 0; +} + +/** + * scols_counter_set_func: + * @ct: counter instance + * @func: SCOLS_COUNTER_{COUNT,MAX,MIN,SUM} + * + * Defines function to calculate data. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_func(struct libscols_counter *ct, int func) +{ + if (!ct || func < 0 || func >= __SCOLS_NCOUNTES) + return -EINVAL; + + ct->func = func; + return 0; +} + +/** + * scols_counter_get_result: + * @ct: counter instance + * + * Returns: result from the counter + * + * Since: 2.40 + */ +unsigned long long scols_counter_get_result(struct libscols_counter *ct) +{ + return ct ? ct->result : 0; +} + +/** + * scols_counter_get_name: + * @ct: counter instance + * + * Returns: name of the counter. + * + * Since: 2.40 + */ +const char *scols_counter_get_name(struct libscols_counter *ct) +{ + return ct ? ct->name : NULL;; +} + +/** + * scols_filter_next_counter: + * @fltr: filter instance + * @itr: a pointer to a struct libscols_iter instance + * @ct: returns the next counter + * + * Finds the next counter and returns a pointer to it via @ct. + * + * Returns: 0, a negative value in case of an error, and 1 at the end. + * + * Since: 2.40 + */ +int scols_filter_next_counter(struct libscols_filter *fltr, + struct libscols_iter *itr, struct libscols_counter **ct) +{ + int rc = 1; + + if (!fltr || !itr || !ct) + return -EINVAL; + *ct = NULL; + + if (!itr->head) + SCOLS_ITER_INIT(itr, &fltr->counters); + if (itr->p != itr->head) { + SCOLS_ITER_ITERATE(itr, *ct, struct libscols_counter, counters); + rc = 0; + } + + return rc; +} -- cgit v1.2.3