]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libsmartcols: implement filter based counters
authorKarel Zak <kzak@redhat.com>
Tue, 26 Sep 2023 10:43:01 +0000 (12:43 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 20 Nov 2023 21:25:46 +0000 (22:25 +0100)
* 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 <kzak@redhat.com>
libsmartcols/src/filter-param.c
libsmartcols/src/filter.c
libsmartcols/src/libsmartcols.h.in
libsmartcols/src/libsmartcols.sym
libsmartcols/src/smartcolsP.h

index 78b6e97c8881cb3a87bb6dbd672f08c2a4039c1c..1c825b501608d94ab41475e5f9921858dc1d1bb3 100644 (file)
@@ -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)
index e8d88d79983a020f2a23164b9218b504e01aa371..df59f6fa56f7aabe066e2b69409d452860ba12ca 100644 (file)
@@ -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;
+}
index 3305a037bccbed68616185198c18fe121d8c61dc..bca08a869d1d4ee5458bf3e41c3b4d7fb2a786dc 100644 (file)
@@ -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
index 1cd7ec62645f3b679354e147c0fad2e67e804032..f5a08f140cc317e43054366a62a6ce53453f51dc 100644 (file)
@@ -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;
index 45e281cb3d50d40f2bd970052114485709e73765..a09f0d465bf835755da821e549d50790e7630839 100644 (file)
@@ -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);