]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libsmartcols: introduce basic files for filter implementation
authorKarel Zak <kzak@redhat.com>
Thu, 20 Jul 2023 21:08:07 +0000 (23:08 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 20 Nov 2023 21:25:46 +0000 (22:25 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
configure.ac
libsmartcols/src/Makemodule.am
libsmartcols/src/filter-nodes.c [new file with mode: 0644]
libsmartcols/src/filter-parser.y [new file with mode: 0644]
libsmartcols/src/filter-scanner.l [new file with mode: 0644]
libsmartcols/src/filter.c [new file with mode: 0644]
libsmartcols/src/smartcolsP.h

index d7c5155c9c09c07fbc56dd975ba875bcdcf74253..cc4af86c182c5968de80fdba645f0535d035248e 100644 (file)
@@ -124,6 +124,7 @@ AC_PROG_CC
 AM_PROG_CC_C_O
 AC_PROG_MKDIR_P
 AC_PROG_YACC
+AM_PROG_LEX
 AC_CANONICAL_HOST
 AC_C_CONST
 AC_C_VOLATILE
index 2bb19fdbde0a991b979cbcdfcdbd779dd0f6f033..51458cbfc2cdea1ba778e10f4ffde20264c5cdb2 100644 (file)
@@ -1,5 +1,4 @@
 
-
 # smartcols.h is generated, so it's stored in builddir!
 smartcolsincdir = $(includedir)/libsmartcols
 nodist_smartcolsinc_HEADERS = libsmartcols/src/libsmartcols.h
@@ -21,7 +20,18 @@ libsmartcols_la_SOURCES= \
        libsmartcols/src/calculate.c \
        libsmartcols/src/grouping.c \
        libsmartcols/src/walk.c \
-       libsmartcols/src/init.c
+       libsmartcols/src/init.c \
+       \
+       libsmartcols/src/filter-nodes.c \
+       libsmartcols/src/filter-parser.y \
+       libsmartcols/src/filter-scanner.l \
+       libsmartcols/src/filter.c
+
+BUILT_SOURCES = libsmartcols/src/filter-scanner.c \
+               libsmartcols/src/filter-scanner.h \
+               libsmartcols/src/filter-parser.h \
+               libsmartcols/src/filter-parser.c
+AM_YFLAGS = -d
 
 libsmartcols_la_LIBADD = $(LDADD) libcommon.la
 
diff --git a/libsmartcols/src/filter-nodes.c b/libsmartcols/src/filter-nodes.c
new file mode 100644 (file)
index 0000000..fadb364
--- /dev/null
@@ -0,0 +1,211 @@
+
+#include "smartcolsP.h"
+
+/* This is generic allocater for a new node, always use the node type specific
+ * functions (e.g. filter_new_param() */
+static struct filter_node *new_node(enum filter_ntype type, size_t sz)
+{
+       void *x = calloc(1, sz);
+       struct filter_node *n = (struct filter_node *) x;
+
+       if (!x)
+               return NULL;
+
+       n->type = type;
+       n->refcount = 1;
+       return n;
+}
+
+struct filter_node *filter_new_param(struct libscols_filter *fltr __attribute__((__unused__)),
+                                enum filter_ptype type,
+                                void *data)
+{
+       struct filter_param *n = (struct filter_param *) new_node(
+                                       F_NODE_PARAM,
+                                       sizeof(struct filter_param));
+       n->type = type;
+
+       switch (type) {
+       case F_PARAM_NAME:
+       case F_PARAM_STRING:
+               n->val.str = strdup((char *) data);
+               break;
+       case F_PARAM_NUMBER:
+               n->val.num = *((unsigned long long *) data);
+               break;
+       case F_PARAM_FLOAT:
+               n->val.fnum = *((long double *) data);
+               break;
+       case F_PARAM_BOOLEAN:
+               n->val.boolean = *((bool *) data) == 0 ? 0 : 1;
+               break;
+       }
+       return (struct filter_node *) n;
+}
+
+static void free_param(struct filter_param *n)
+{
+       if (n->type == F_PARAM_NAME || n->type == F_PARAM_STRING)
+               free(n->val.str);
+       free(n);
+}
+
+#define plus_indent(i)         ((i) + 5)
+#define indent(f, i)           fprintf(f, "%*s", (i), "");
+#define indent_inside(f, i)    indent(f, plus_indent(i))
+
+static void dump_param(FILE *out, int i __attribute__((__unused__)), struct filter_param *n)
+{
+       fprintf(out, "param { ");
+
+       switch (n->type) {
+       case F_PARAM_NAME:
+               fprintf(out, "name: '%s'", n->val.str);
+               break;
+       case F_PARAM_STRING:
+               fprintf(out, "string: '%s'", n->val.str);
+               break;
+       case F_PARAM_NUMBER:
+               fprintf(out, "number: %llu", n->val.num);
+               break;
+       case F_PARAM_FLOAT:
+               fprintf(out, "float: %Lg", n->val.fnum);
+               break;
+       case F_PARAM_BOOLEAN:
+               fprintf(out, "bool: %s", n->val.boolean ? "true" : "false");
+               break;
+       }
+       fprintf(out, " }\n");
+}
+
+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 *) new_node(
+                                       F_NODE_EXPR, sizeof(struct filter_expr));
+
+       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;
+}
+
+static void free_expr(struct filter_expr *n)
+{
+       filter_unref_node(n->left);
+       filter_unref_node(n->right);
+       free(n);
+}
+
+static void dump_expr(FILE *out, int i, struct filter_expr *n)
+{
+       fprintf(out, "expr {\n");
+
+       indent_inside(out, i);
+       fprintf(out, "type: ");
+
+       switch (n->type) {
+       case F_EXPR_AND:
+               fprintf(out, "AND");
+               break;
+       case F_EXPR_OR:
+               fprintf(out, "OR");
+               break;
+       case F_EXPR_EQ:
+               fprintf(out, "EQ");
+               break;
+       case F_EXPR_NE:
+               fprintf(out, "NE");
+               break;
+       case F_EXPR_LE:
+               fprintf(out, "LE");
+               break;
+       case F_EXPR_LT:
+               fprintf(out, "LT");
+               break;
+       case F_EXPR_GE:
+               fprintf(out, "GE");
+               break;
+       case F_EXPR_GT:
+               fprintf(out, "GT");
+               break;
+       case F_EXPR_REG:
+               fprintf(out, "REG");
+               break;
+       case F_EXPR_NREG:
+               fprintf(out, "NREG");
+               break;
+       case F_EXPR_NEG:
+               fprintf(out, "NOT");
+               break;
+       }
+
+       fprintf(out, "\n");
+
+       if (n->left) {
+               indent_inside(out, i);
+               fprintf(out, "left: ");
+               filter_dump_node(out, plus_indent(i), n->left);
+       }
+
+       if (n->right) {
+               indent_inside(out, i);
+               fprintf(out, "right: ");
+               filter_dump_node(out, plus_indent(i), n->right);
+       }
+
+       indent(out, i);
+       fprintf(out, "}\n");
+}
+
+
+void filter_unref_node(struct filter_node *n)
+{
+       if (!n || --n->refcount > 0)
+               return;
+
+       switch (n->type) {
+       case F_NODE_EXPR:
+               free_expr((struct filter_expr *) n);
+               break;
+       case F_NODE_PARAM:
+               free_param((struct filter_param *) n);
+               break;
+       }
+}
+
+void filter_dump_node(FILE *out, int i, struct filter_node *n)
+{
+       if (!n)
+               return;
+
+       switch (n->type) {
+       case F_NODE_EXPR:
+               dump_expr(out, i, (struct filter_expr *) n);
+               break;
+       case F_NODE_PARAM:
+               dump_param(out, i, (struct filter_param *) n);
+               break;
+       }
+       if (i == 0)
+               fprintf(out, "\n");
+}
diff --git a/libsmartcols/src/filter-parser.y b/libsmartcols/src/filter-parser.y
new file mode 100644 (file)
index 0000000..4c93a00
--- /dev/null
@@ -0,0 +1,100 @@
+%{
+#include <stdio.h>
+#include "smartcolsP.h"
+
+#include "filter-parser.h"
+#include "filter-scanner.h"
+
+void yyerror(yyscan_t *locp, struct libscols_filter *fltr, char const *msg);
+%}
+
+%define api.pure full
+
+%lex-param {void *scanner}
+%parse-param {void *scanner}{struct libscols_filter *fltr}
+
+%define parse.trace
+%define parse.error verbose
+
+%header "filter-parser.h"
+
+%code requires
+{
+       #include "smartcolsP.h"
+}
+
+/* Elegant way, but not compatible with biron -y (autotools):
+%define api.value.type union
+%token <unsigned long long>    param_number
+%token <const char*>           param_string
+%token <const char*>           param_name
+%token <long double>           param_float
+%type <struct filter_node*> param
+%type <struct filter_node*> expr
+*/
+
+%union {
+       unsigned long long      param_number;
+       const char*             param_string;
+       const char*             param_name;
+       long double             param_float;
+       struct filter_node      *param;
+       struct filter_node      *expr;
+}
+%token <param_number> param_number
+%token <param_string> param_string
+%token <param_name> param_name
+%token <param_float> param_float
+%type <param> param expr
+
+%token T_OR T_AND T_EQ T_NE T_LT T_LE T_GT T_GE T_REG T_NREG T_TRUE T_FALSE T_NEG
+%left T_OR T_AND
+%left T_EQ T_NE T_LT T_LE T_GT T_GE T_REG T_NREG T_TRUE T_FALSE T_NEG
+
+%%
+
+%start filter;
+
+filter:
+       expr { fltr->root = $1; }
+;
+
+expr:
+       param                   { $$ = $1; }
+       | '(' expr ')'          { $$ = $2; }
+       | expr T_AND expr       { $$ = filter_new_expr(fltr, F_EXPR_AND, $1, $3); }
+       | expr T_OR expr        { $$ = filter_new_expr(fltr, F_EXPR_OR, $1, $3); }
+       | T_NEG expr            { $$ = filter_new_expr(fltr, F_EXPR_NEG, NULL, $2); }
+       | expr T_EQ expr        { $$ = filter_new_expr(fltr, F_EXPR_EQ, $1, $3); }
+       | expr T_NE expr        { $$ = filter_new_expr(fltr, F_EXPR_NE, $1, $3); }
+       | expr T_LE expr        { $$ = filter_new_expr(fltr, F_EXPR_LE, $1, $3); }
+       | expr T_LT expr        { $$ = filter_new_expr(fltr, F_EXPR_LT, $1, $3); }
+       | expr T_GE expr        { $$ = filter_new_expr(fltr, F_EXPR_GE, $1, $3); }
+       | expr T_GT expr        { $$ = filter_new_expr(fltr, F_EXPR_GT, $1, $3); }
+       | expr T_REG expr       { $$ = filter_new_expr(fltr, F_EXPR_REG, $1, $3); }
+       | expr T_NREG expr      { $$ = filter_new_expr(fltr, F_EXPR_NREG, $1, $3); }
+;
+
+param:
+       param_number    { $$ = filter_new_param(fltr, F_PARAM_NUMBER, (void *) (&$1)); }
+       | param_float   { $$ = filter_new_param(fltr, F_PARAM_FLOAT, (void *) (&$1)); }
+       | param_name    { $$ = filter_new_param(fltr, F_PARAM_NAME, (void *) $1); }
+       | param_string  { $$ = filter_new_param(fltr, F_PARAM_STRING, (void *) $1); }
+       | T_TRUE        {
+               bool x = true;
+               $$ = filter_new_param(fltr, F_PARAM_BOOLEAN, (void *) &x);
+       }
+       | T_FALSE       {
+               bool x = false;
+               $$ = filter_new_param(fltr, F_PARAM_BOOLEAN, (void *) &x);
+       }
+
+;
+
+
+%%
+
+void yyerror (yyscan_t *locp, struct libscols_filter *fltr, char const *msg)
+{
+       fprintf(stderr, "--> %s\n", msg);
+}
diff --git a/libsmartcols/src/filter-scanner.l b/libsmartcols/src/filter-scanner.l
new file mode 100644 (file)
index 0000000..71ce3d1
--- /dev/null
@@ -0,0 +1,58 @@
+%{
+#include "filter-parser.h"     /* define tokens (T_*) */
+%}
+
+%option reentrant bison-bridge noyywrap noinput nounput
+
+id     [a-zA-Z][a-zA-Z_0-9]*
+int    [0-9]+
+blank  [ \t]
+string \"[^\"\n]*\"
+
+%%
+
+{blank}+       ;       /* ignore */
+[\n]+          ;       /* ignore */
+
+"("            return '(';
+")"            return ')';
+"'"            return '\'';
+
+false|FALSE    return T_FALSE;
+true|TRUE      return T_TRUE;
+
+{int}+\.{int}+ {
+       yylval->param_float = strtold(yytext, NULL);
+       return param_float;
+}
+
+{int}+ {
+       yylval->param_number = (int64_t) strtoumax(yytext, NULL, 10);
+       return param_number;
+}
+
+{id} {
+       yylval->param_name = yytext;
+       return param_name;
+}
+
+{string} {
+       yylval->param_string = yytext;
+       return param_string;
+}
+
+and|AND|"&&"   return T_AND;
+or|OR|"||"     return T_OR;
+"!"|not|NOT    return T_NEG;
+
+eq|"=="                return T_EQ;
+ne|"!="                return T_NE;
+
+le|"<="                return T_LE;
+lt|"<"         return T_LT;
+
+ge|">="                return T_GE;
+gt|">"         return T_GT;
+
+"=~"           return T_REG;
+"!~"           return T_NREG;
diff --git a/libsmartcols/src/filter.c b/libsmartcols/src/filter.c
new file mode 100644 (file)
index 0000000..7450150
--- /dev/null
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "smartcolsP.h"
+
+#include "filter-parser.h"
+#include "filter-scanner.h"
+
+struct libscols_filter *scols_new_filter()
+{
+       struct libscols_filter *fltr = calloc(1, sizeof(*fltr));
+
+       return fltr;
+}
+
+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;
+}
+
+int scols_filter_parse_string(struct libscols_filter *fltr, const char *string)
+{
+       yyscan_t sc;
+       int rc;
+
+       reset_filter(fltr);
+
+       fltr->src = fmemopen((void *) string, strlen(string) + 1, "r");
+       if (!fltr->src)
+               return -errno;
+
+       yylex_init(&sc);
+       yyset_in(fltr->src, sc);
+
+       rc = yyparse(sc, fltr);
+       yylex_destroy(sc);
+
+       return rc;
+}
+
+/* TODO:
+
+scols_dump_filter()
+scols_line_apply_filter()
+scols_table_apply_filter()
+ */
index afb0c4b77dda57410d156cb51680f90cf7709f0a..1b476f8599deecb849882f53ec31a00efd1b19fc 100644 (file)
@@ -19,6 +19,8 @@
 #include "debug.h"
 #include "buffer.h"
 
+#include <stdbool.h>
+
 #include "libsmartcols.h"
 
 /*
@@ -475,4 +477,85 @@ static inline int has_group_children(struct libscols_line *ln)
        return ln && ln->group && !list_empty(&ln->group->gr_children);
 }
 
+/*
+ * Filter stuff
+ */
+
+/* node types */
+enum filter_ntype {
+       F_NODE_PARAM,
+       F_NODE_EXPR
+};
+
+/* param types */
+enum filter_ptype {
+       F_PARAM_NUMBER,
+       F_PARAM_FLOAT,
+       F_PARAM_NAME,
+       F_PARAM_STRING,
+       F_PARAM_BOOLEAN
+};
+
+/* expresion types */
+enum filter_etype {
+       F_EXPR_AND,
+       F_EXPR_OR,
+       F_EXPR_NEG,
+
+       F_EXPR_EQ,
+       F_EXPR_NE,
+
+       F_EXPR_LT,
+       F_EXPR_LE,
+       F_EXPR_GT,
+       F_EXPR_GE,
+
+       F_EXPR_REG,
+       F_EXPR_NREG,
+};
+
+struct filter_node {
+       enum filter_ntype type;
+       int refcount;
+};
+
+#define filter_node_get_type(n)        (((struct filter_node *)(n))->type)
+
+struct filter_param {
+       struct filter_node node;
+       enum filter_ptype type;
+
+       union {
+               char *str;
+               unsigned long long num;
+               long double fnum;
+               bool boolean;
+       } val;
+};
+
+struct filter_expr {
+       struct filter_node node;
+       enum filter_etype type;
+
+       struct filter_node *left;
+       struct filter_node *right;
+};
+
+struct libscols_filter {
+       struct filter_node *root;
+       FILE *src;
+};
+
+void filter_unref_node(struct filter_node *n);
+void filter_dump_node(FILE *out, int i, struct filter_node *n);
+
+/* reuiqred by parser */
+struct filter_node *filter_new_param(struct libscols_filter *filter,
+                                 enum filter_ptype type,
+                                void *data);
+struct filter_node *filter_new_expr(struct libscols_filter *filter,
+                                 enum filter_etype type,
+                                 struct filter_node *left,
+                                 struct filter_node *right);
+
 #endif /* _LIBSMARTCOLS_PRIVATE_H */