summaryrefslogtreecommitdiffstats
path: root/libsmartcols/src/filter-expr.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:33:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:33:30 +0000
commitc61e14d3a8412cd50d98aab604e607692c844c8a (patch)
tree4925aca0e6b64c8664ea2f3fdfa99a52dc93d5da /libsmartcols/src/filter-expr.c
parentAdding upstream version 2.39.3. (diff)
downloadutil-linux-c61e14d3a8412cd50d98aab604e607692c844c8a.tar.xz
util-linux-c61e14d3a8412cd50d98aab604e607692c844c8a.zip
Adding upstream version 2.40.upstream/2.40
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libsmartcols/src/filter-expr.c')
-rw-r--r--libsmartcols/src/filter-expr.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/libsmartcols/src/filter-expr.c b/libsmartcols/src/filter-expr.c
new file mode 100644
index 0000000..7e559c4
--- /dev/null
+++ b/libsmartcols/src/filter-expr.c
@@ -0,0 +1,219 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "smartcolsP.h"
+
+struct filter_expr {
+ struct filter_node node;
+ enum filter_etype type;
+
+ struct filter_node *left;
+ struct filter_node *right;
+};
+
+struct filter_node *filter_new_expr(
+ struct libscols_filter *fltr __attribute__((__unused__)),
+ enum filter_etype type,
+ struct filter_node *left,
+ struct filter_node *right)
+{
+ struct filter_expr *n = (struct filter_expr *) __filter_new_node(
+ F_NODE_EXPR, sizeof(struct filter_expr));
+
+ if (!n)
+ return NULL;
+
+ n->type = type;
+
+ switch (type) {
+ case F_EXPR_AND:
+ case F_EXPR_OR:
+ case F_EXPR_EQ:
+ case F_EXPR_NE:
+ case F_EXPR_LE:
+ case F_EXPR_LT:
+ case F_EXPR_GE:
+ case F_EXPR_GT:
+ case F_EXPR_REG:
+ case F_EXPR_NREG:
+ n->left = left;
+ n->right = right;
+ break;
+ case F_EXPR_NEG:
+ n->right = right;
+ break;
+ }
+
+ return (struct filter_node *) n;
+}
+
+void filter_free_expr(struct filter_expr *n)
+{
+ filter_unref_node(n->left);
+ filter_unref_node(n->right);
+ free(n);
+}
+
+static const char *expr_type_as_string(struct filter_expr *n)
+{
+ switch (n->type) {
+ case F_EXPR_AND:
+ return "AND";
+ case F_EXPR_OR:
+ return "OR";
+ case F_EXPR_EQ:
+ return "EQ";
+ case F_EXPR_NE:
+ return "NE";
+ case F_EXPR_LE:
+ return "LE";
+ case F_EXPR_LT:
+ return "LT";
+ case F_EXPR_GE:
+ return "GE";
+ case F_EXPR_GT:
+ return "GT";
+ case F_EXPR_REG:
+ return "REG";
+ case F_EXPR_NREG:
+ return "NREG";
+ case F_EXPR_NEG:
+ return "NOT";
+ }
+ return "";
+}
+
+void filter_dump_expr(struct ul_jsonwrt *json, struct filter_expr *n)
+{
+ ul_jsonwrt_object_open(json, "expr");
+ ul_jsonwrt_value_s(json, "type", expr_type_as_string(n));
+
+ if (n->left)
+ filter_dump_node(json, n->left);
+ if (n->right)
+ filter_dump_node(json, n->right);
+
+ ul_jsonwrt_object_close(json);
+}
+
+static int cast_node(struct libscols_filter *fltr,
+ struct libscols_line *ln,
+ int type,
+ struct filter_node *n,
+ struct filter_param **result)
+{
+ struct filter_node *pr;
+ int status = 0, rc;
+ bool x;
+
+ switch (n->type) {
+ case F_NODE_EXPR:
+ /* convert expression to a boolean param */
+ rc = filter_eval_expr(fltr, ln, (struct filter_expr *) n, &status);
+ if (rc)
+ return rc;
+ x = status != 0 ? true : false;
+ pr = filter_new_param(NULL, SCOLS_DATA_BOOLEAN, 0, (void *) &x);
+ if (!pr)
+ return -ENOMEM;
+ rc = filter_cast_param(fltr, ln, type, (struct filter_param *) pr, result);
+ filter_unref_node(pr);
+ break;
+ case F_NODE_PARAM:
+ rc = filter_cast_param(fltr, ln, type, (struct filter_param *) n, result);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int node_get_datatype(struct filter_node *n)
+{
+ switch (n->type) {
+ case F_NODE_EXPR:
+ return SCOLS_DATA_BOOLEAN;
+ case F_NODE_PARAM:
+ return filter_param_get_datatype((struct filter_param *) n);
+ }
+ return SCOLS_DATA_NONE;
+}
+
+static int guess_expr_datatype(struct filter_expr *n)
+{
+ int type;
+ int l = node_get_datatype(n->left),
+ r = node_get_datatype(n->right);
+
+ if (l == r)
+ type = l;
+ else {
+ bool l_holder, r_holder;
+
+ /* for expression like "FOO > 5.5" preffer type defined by a real param
+ * rather than by holder (FOO) */
+ l_holder = is_filter_holder_node(n->left);
+ r_holder = is_filter_holder_node(n->right);
+
+ if (l_holder && !r_holder)
+ type = r;
+ else if (r_holder && !l_holder)
+ type = l;
+ else
+ type = l;
+
+ /* Always prefer float before number */
+ if (type == SCOLS_DATA_U64
+ && (r == SCOLS_DATA_FLOAT || l == SCOLS_DATA_FLOAT))
+ type = SCOLS_DATA_FLOAT;
+ }
+
+ DBG(FPARAM, ul_debugobj(n, " expr datatype: %d", type));
+ return type;
+}
+
+int filter_eval_expr(struct libscols_filter *fltr, struct libscols_line *ln,
+ struct filter_expr *n, int *status)
+{
+ int rc = 0;
+ struct filter_param *l = NULL, *r = NULL;
+ enum filter_etype oper = n->type;
+ int type;
+
+ /* logical operators */
+ switch (oper) {
+ case F_EXPR_AND:
+ rc = filter_eval_node(fltr, ln, n->left, status);
+ if (rc == 0 && *status)
+ rc = filter_eval_node(fltr, ln, n->right, status);
+ return rc;
+ case F_EXPR_OR:
+ rc = filter_eval_node(fltr, ln, n->left, status);
+ if (rc == 0 && !*status)
+ rc = filter_eval_node(fltr, ln, n->right, status);
+ return rc;
+ case F_EXPR_NEG:
+ rc = filter_eval_node(fltr, ln, n->right, status);
+ if (rc == 0)
+ *status = !*status;
+ return rc;
+ default:
+ break;
+ }
+
+ type = guess_expr_datatype(n);
+
+ /* compare data */
+ rc = cast_node(fltr, ln, type, n->left, &l);
+ if (!rc)
+ rc = cast_node(fltr, ln, type, n->right, &r);
+ if (!rc)
+ rc = filter_compare_params(fltr, oper, l, r, status);
+
+ filter_unref_node((struct filter_node *) l);
+ filter_unref_node((struct filter_node *) r);
+ return rc;
+}