From: Karel Zak Date: Tue, 26 Sep 2023 10:43:01 +0000 (+0200) Subject: libsmartcols: implement filter based counters X-Git-Tag: v2.40-rc1~151^2~55 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3aeacd6a47df358da0b945496e5e29c5063aca4d;p=thirdparty%2Futil-linux.git libsmartcols: implement filter based counters * add support for empty filter; the result of the empty expression is always "true" * Filter can hold arbitrary number of counters * A counter is based on a built-in function. Now supported are count, max, min and sum functions. * The default function is "count" (count lines) * It's possible to assign column (param) to the counter, for example count sum of all data in the column * The param is used in the same way as params in the filter (and initialized in the same way) Based on Masatake YAMATO's work on lsfd. Signed-off-by: Karel Zak --- diff --git a/libsmartcols/src/filter-param.c b/libsmartcols/src/filter-param.c index 78b6e97c88..1c825b5016 100644 --- a/libsmartcols/src/filter-param.c +++ b/libsmartcols/src/filter-param.c @@ -317,6 +317,51 @@ done: return rc; } +int filter_count_param(struct libscols_filter *fltr, + struct libscols_line *ln, + struct libscols_counter *ct) +{ + unsigned long long num; + + + if (ct->func == SCOLS_COUNTER_COUNT) { + ct->result++; + return 0; + } + + if (ct->param) { + int rc; + + ct->param->type = F_DATA_NUMBER; + rc = fetch_holder_data(fltr, ct->param, ln); + if (rc) + return rc; + } + if (!ct->param->has_value) + return -EINVAL; + + num = ct->param->val.num; + + switch (ct->func) { + case SCOLS_COUNTER_MAX: + if (num > ct->result) + ct->result = num; + break; + case SCOLS_COUNTER_MIN: + if (num < ct->result) + ct->result = num; + break; + case SCOLS_COUNTER_SUM: + ct->result += num; + break; + default: + return -EINVAL; + } + + DBG(FLTR, ul_debugobj(fltr, "counted '%s' [result: %llu]", ct->name, ct->result)); + return 0; +} + static int xstrcmp(char *a, char *b) { if (!a && !b) diff --git a/libsmartcols/src/filter.c b/libsmartcols/src/filter.c index e8d88d7998..df59f6fa56 100644 --- a/libsmartcols/src/filter.c +++ b/libsmartcols/src/filter.c @@ -14,8 +14,11 @@ struct libscols_filter *scols_new_filter(const char *str) 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); @@ -46,11 +49,29 @@ static void reset_filter(struct libscols_filter *fltr) 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); + } +} + 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); } } @@ -117,6 +138,9 @@ int scols_filter_parse_string(struct libscols_filter *fltr, const char *str) reset_filter(fltr); + if (!str || !*str) + return 0; /* empty filter is not error */ + fltr->src = fmemopen((void *) str, strlen(str) + 1, "r"); if (!fltr->src) return -errno; @@ -170,11 +194,11 @@ int filter_eval_node(struct libscols_filter *fltr, struct libscols_line *ln, int scols_line_apply_filter(struct libscols_line *ln, struct libscols_filter *fltr, int *status) { - int rc; + int rc, res = 0; struct libscols_iter itr; struct filter_param *prm = NULL; - if (!ln || !fltr || !fltr->root) + if (!ln || !fltr) return -EINVAL; /* reset column data and types stored in the filter */ @@ -183,9 +207,24 @@ int scols_line_apply_filter(struct libscols_line *ln, filter_param_reset_holder(prm); } - rc = filter_eval_node(fltr, ln, fltr->root, status); + 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; - DBG(FLTR, ul_debugobj(fltr, "filter done [rc=%d, status=%d]", rc, *status)); + 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; } @@ -201,3 +240,88 @@ int scols_filter_set_filler_cb(struct libscols_filter *fltr, return 0; } + +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; +} + +int scols_counter_set_name(struct libscols_counter *ct, const char *name) +{ + if (!ct) + return -EINVAL; + return strdup_to_struct_member(ct, name, name); +} + +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, F_DATA_NUMBER, + F_HOLDER_COLUMN, (void *) name); + if (!ct->param) + return -ENOMEM; + } + return 0; +} + +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; +} + +unsigned long long scols_counter_get_result(struct libscols_counter *ct) +{ + return ct ? ct->result : 0; +} + +const char *scols_counter_get_name(struct libscols_counter *ct) +{ + return ct ? ct->name : NULL;; +} + +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; +} diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in index 3305a037bc..bca08a869d 100644 --- a/libsmartcols/src/libsmartcols.h.in +++ b/libsmartcols/src/libsmartcols.h.in @@ -74,6 +74,22 @@ struct libscols_column; */ struct libscols_filter; +/** + * libscols_counter: + * + * A filter counter + */ +struct libscols_counter; + +enum { + SCOLS_COUNTER_COUNT = 0, + SCOLS_COUNTER_MAX, + SCOLS_COUNTER_MIN, + SCOLS_COUNTER_SUM, + + __SCOLS_NCOUNTES +}; + /* iter.c */ enum { @@ -389,6 +405,16 @@ extern int scols_filter_set_filler_cb(struct libscols_filter *fltr, struct libscols_line *, size_t, void *), void *userdata); +extern struct libscols_counter *scols_filter_new_counter(struct libscols_filter *fltr); +extern int scols_counter_set_name(struct libscols_counter *ct, const char *name); +extern int scols_counter_set_param(struct libscols_counter *ct, const char *name); +extern int scols_counter_set_func(struct libscols_counter *ct, int func); + +extern unsigned long long scols_counter_get_result(struct libscols_counter *ct); +extern const char *scols_counter_get_name(struct libscols_counter *ct); +extern int scols_filter_next_counter(struct libscols_filter *fltr, + struct libscols_iter *itr, struct libscols_counter **ct); + #ifdef __cplusplus } #endif diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym index 1cd7ec6264..f5a08f140c 100644 --- a/libsmartcols/src/libsmartcols.sym +++ b/libsmartcols/src/libsmartcols.sym @@ -231,4 +231,11 @@ SMARTCOLS_2.40 { scols_line_apply_filter; scols_filter_set_filler_cb; scols_line_is_filled; + scols_filter_new_counter; + scols_counter_set_name; + scols_counter_set_param; + scols_counter_set_func; + scols_counter_get_result; + scols_counter_get_name; + scols_filter_next_counter; } SMARTCOLS_2.39; diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 45e281cb3d..a09f0d465b 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -532,6 +532,18 @@ struct filter_node { struct filter_param; struct filter_expr; +struct libscols_counter { + char *name; + struct list_head counters; + struct filter_param *param; + struct libscols_filter *filter; + + int func; + unsigned long long result; + + unsigned int neg : 1; +}; + struct libscols_filter { int refcount; char *errmsg; @@ -542,6 +554,7 @@ struct libscols_filter { void *filler_data; struct list_head params; + struct list_head counters; }; struct filter_node *__filter_new_node(enum filter_ntype type, size_t sz); @@ -576,6 +589,10 @@ int filter_cast_param(struct libscols_filter *fltr, int is_filter_holder_node(struct filter_node *n); +int filter_count_param(struct libscols_filter *fltr, + struct libscols_line *ln, + struct libscols_counter *ct); + /* expr */ void filter_free_expr(struct filter_expr *n); void filter_dump_expr(struct ul_jsonwrt *json, struct filter_expr *n);