From: Karel Zak Date: Tue, 8 Aug 2023 10:18:56 +0000 (+0200) Subject: libsmartcols: improve filter integration, use JSON to dump X-Git-Tag: v2.40-rc1~151^2~84 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=322779d7576ce7ba63b470f18965e6881455077b;p=thirdparty%2Futil-linux.git libsmartcols: improve filter integration, use JSON to dump Signed-off-by: Karel Zak --- diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am index 51458cbfc2..4c373358fc 100644 --- a/libsmartcols/src/Makemodule.am +++ b/libsmartcols/src/Makemodule.am @@ -22,16 +22,25 @@ libsmartcols_la_SOURCES= \ libsmartcols/src/walk.c \ libsmartcols/src/init.c \ \ - libsmartcols/src/filter-nodes.c \ libsmartcols/src/filter-parser.y \ libsmartcols/src/filter-scanner.l \ + libsmartcols/src/filter-parser.h \ + libsmartcols/src/filter-scanner.h \ 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 +BUILT_SOURCES += libsmartcols/src/filter-parser.c \ + libsmartcols/src/filter-scanner.c + + +# Scanner and parser header files are maintained in git and generated +# manually by only if necessary. +# +libsmartcols/src/filter-scanner.h: libsmartcols/src/filter-scanner.l + $(AM_V_LEX) $(LEX) --header-file=$@ $< + +libsmartcols/src/filter-parser.h: libsmartcols/src/filter-parser.y + $(AM_V_YACC) $(YACC) --header=$@ $< + libsmartcols_la_LIBADD = $(LDADD) libcommon.la diff --git a/libsmartcols/src/filter-nodes.c b/libsmartcols/src/filter-nodes.c deleted file mode 100644 index fadb364a57..0000000000 --- a/libsmartcols/src/filter-nodes.c +++ /dev/null @@ -1,211 +0,0 @@ - -#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 index 4c93a00961..69506ae2bb 100644 --- a/libsmartcols/src/filter-parser.y +++ b/libsmartcols/src/filter-parser.y @@ -13,10 +13,9 @@ void yyerror(yyscan_t *locp, struct libscols_filter *fltr, char const *msg); %lex-param {void *scanner} %parse-param {void *scanner}{struct libscols_filter *fltr} -%define parse.trace %define parse.error verbose -%header "filter-parser.h" +/*%header "filter-parser.h"*/ %code requires { @@ -41,10 +40,10 @@ void yyerror(yyscan_t *locp, struct libscols_filter *fltr, char const *msg); struct filter_node *param; struct filter_node *expr; } -%token param_number -%token param_string -%token param_name -%token param_float +%token T_NUMBER +%token T_STRING +%token T_NAME +%token T_FLOAT %type 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 @@ -76,10 +75,10 @@ expr: ; 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_NUMBER { $$ = filter_new_param(fltr, F_PARAM_NUMBER, (void *) (&$1)); } + | T_FLOAT { $$ = filter_new_param(fltr, F_PARAM_FLOAT, (void *) (&$1)); } + | T_NAME { $$ = filter_new_param(fltr, F_PARAM_NAME, (void *) $1); } + | T_STRING { $$ = filter_new_param(fltr, F_PARAM_STRING, (void *) $1); } | T_TRUE { bool x = true; $$ = filter_new_param(fltr, F_PARAM_BOOLEAN, (void *) &x); @@ -94,7 +93,14 @@ param: %% -void yyerror (yyscan_t *locp, struct libscols_filter *fltr, char const *msg) +void yyerror (yyscan_t *locp __attribute__((__unused__)), + struct libscols_filter *fltr, + char const *msg) { - fprintf(stderr, "--> %s\n", msg); + if (msg) { + fltr->errmsg = strdup(msg); + if (!fltr->errmsg) + return; + } + errno = EINVAL; } diff --git a/libsmartcols/src/filter-scanner.l b/libsmartcols/src/filter-scanner.l index 71ce3d185e..c8b610e41c 100644 --- a/libsmartcols/src/filter-scanner.l +++ b/libsmartcols/src/filter-scanner.l @@ -18,41 +18,43 @@ string \"[^\"\n]*\" ")" return ')'; "'" return '\''; +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; + false|FALSE return T_FALSE; true|TRUE return T_TRUE; {int}+\.{int}+ { yylval->param_float = strtold(yytext, NULL); - return param_float; + return T_FLOAT; } {int}+ { yylval->param_number = (int64_t) strtoumax(yytext, NULL, 10); - return param_number; + return T_NUMBER; } {id} { yylval->param_name = yytext; - return param_name; + return T_NAME; } {string} { yylval->param_string = yytext; - return param_string; + return T_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 index 74501501a6..30d601a686 100644 --- a/libsmartcols/src/filter.c +++ b/libsmartcols/src/filter.c @@ -8,13 +8,31 @@ #include "filter-parser.h" #include "filter-scanner.h" -struct libscols_filter *scols_new_filter() +static void filter_unref_node(struct filter_node *n); +static void filter_dump_node(struct ul_jsonwrt *json, struct filter_node *n); + +struct libscols_filter *scols_new_filter(const char *str) { struct libscols_filter *fltr = calloc(1, sizeof(*fltr)); + if (!fltr) + return NULL; + fltr->refcount = 1; + + if (str && scols_filter_parse_string(fltr, str) != 0) { + scols_unref_filter(fltr); + return NULL; + } + return fltr; } +void scols_ref_filter(struct libscols_filter *fltr) +{ + if (fltr) + fltr->refcount++; +} + static void reset_filter(struct libscols_filter *fltr) { if (!fltr) @@ -25,16 +43,233 @@ static void reset_filter(struct libscols_filter *fltr) if (fltr->src) fclose(fltr->src); fltr->src = NULL; + + free(fltr->errmsg); + fltr->errmsg = NULL; +} + +void scols_unref_filter(struct libscols_filter *fltr) +{ + if (fltr && --fltr->refcount <= 0) { + DBG(FLTR, ul_debugobj(fltr, "dealloc")); + reset_filter(fltr); + free(fltr); + } +} + +/* 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) +{ + char *p; + struct filter_param *n = (struct filter_param *) new_node( + F_NODE_PARAM, + sizeof(struct filter_param)); + n->type = type; + + switch (type) { + case F_PARAM_STRING: + p = data; + if (*p == '"') { + /* remove quotation marks */ + size_t len = strlen(p); + if (*(p + (len - 1)) == '"') + *(p + (len - 1)) = '\0'; + data = p + 1; + } + /* fallthrough */ + case F_PARAM_NAME: + 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); +} + +static void dump_param(struct ul_jsonwrt *json, struct filter_param *n) +{ + ul_jsonwrt_object_open(json, "param"); + + switch (n->type) { + case F_PARAM_NAME: + ul_jsonwrt_value_s(json, "name", n->val.str); + break; + case F_PARAM_STRING: + ul_jsonwrt_value_s(json, "string", n->val.str); + break; + case F_PARAM_NUMBER: + ul_jsonwrt_value_u64(json, "number", n->val.num); + break; + case F_PARAM_FLOAT: + ul_jsonwrt_value_double(json, "float", n->val.fnum); + break; + case F_PARAM_BOOLEAN: + ul_jsonwrt_value_boolean(json, "bool", n->val.boolean); + break; + } + ul_jsonwrt_object_close(json); +} + +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 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 ""; } -int scols_filter_parse_string(struct libscols_filter *fltr, const char *string) +static void 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 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; + } +} + +/* +static void filter_ref_node(struct filter_node *n) +{ + if (n) + n->refcount++; +} +*/ + +static void filter_dump_node(struct ul_jsonwrt *json, struct filter_node *n) +{ + if (!n) + return; + + switch (n->type) { + case F_NODE_EXPR: + dump_expr(json, (struct filter_expr *) n); + break; + case F_NODE_PARAM: + dump_param(json, (struct filter_param *) n); + break; + } +} + +extern int yyparse(void *scanner, struct libscols_filter *fltr); + +int scols_filter_parse_string(struct libscols_filter *fltr, const char *str) { yyscan_t sc; int rc; reset_filter(fltr); - fltr->src = fmemopen((void *) string, strlen(string) + 1, "r"); + fltr->src = fmemopen((void *) str, strlen(str) + 1, "r"); if (!fltr->src) return -errno; @@ -44,12 +279,28 @@ int scols_filter_parse_string(struct libscols_filter *fltr, const char *string) rc = yyparse(sc, fltr); yylex_destroy(sc); + fclose(fltr->src); + fltr->src = NULL; + return rc; } -/* TODO: +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); -scols_dump_filter() -scols_line_apply_filter() -scols_table_apply_filter() - */ + filter_dump_node(&json, fltr->root); + ul_jsonwrt_root_close(&json); + return 0; +} + +const char *scols_filter_get_errmsg(struct libscols_filter *fltr) +{ + return fltr ? fltr->errmsg : NULL; +} diff --git a/libsmartcols/src/init.c b/libsmartcols/src/init.c index dfd7510dcd..cd5e05db6d 100644 --- a/libsmartcols/src/init.c +++ b/libsmartcols/src/init.c @@ -28,6 +28,7 @@ UL_DEBUG_DEFINE_MASKNAMES(libsmartcols) = { "group", SCOLS_DEBUG_GROUP, "lines grouping utils" }, { "line", SCOLS_DEBUG_LINE, "table line utils" }, { "tab", SCOLS_DEBUG_TAB, "table utils" }, + { "filter", SCOLS_DEBUG_FLTR, "lines filter" }, { NULL, 0, NULL } }; diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in index d58c4fd1de..2262cb149c 100644 --- a/libsmartcols/src/libsmartcols.h.in +++ b/libsmartcols/src/libsmartcols.h.in @@ -67,6 +67,13 @@ struct libscols_table; */ struct libscols_column; +/** + * libscols_filter: + * + * A filter - defines the filtering + */ +struct libscols_filter; + /* iter.c */ enum { @@ -358,6 +365,15 @@ extern int scols_table_print_range_to_string( struct libscols_table *tb, int scols_line_link_group(struct libscols_line *ln, struct libscols_line *member, int id); int scols_table_group_lines(struct libscols_table *tb, struct libscols_line *ln, struct libscols_line *member, int id); + +/* filter.c */ +extern int scols_filter_parse_string(struct libscols_filter *fltr, const char *str); +extern struct libscols_filter *scols_new_filter(const char *str); +extern void scols_ref_filter(struct libscols_filter *fltr); +extern void scols_unref_filter(struct libscols_filter *fltr); +extern int scols_dump_filter(struct libscols_filter *fltr, FILE *out); +extern const char *scols_filter_get_errmsg(struct libscols_filter *fltr); + #ifdef __cplusplus } #endif diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym index 2881169117..4fb97f43e7 100644 --- a/libsmartcols/src/libsmartcols.sym +++ b/libsmartcols/src/libsmartcols.sym @@ -221,4 +221,9 @@ SMARTCOLS_2.40 { scols_cell_get_datasiz; scols_wrapzero_nextchunk; scols_column_get_wrap_data; + scols_dump_filter; + scols_filter_parse_string; + scols_new_filter; + scols_unref_filter; + scols_filter_get_errmsg; } SMARTCOLS_2.39; diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 1b476f8599..86c07ea40e 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -34,6 +34,7 @@ #define SCOLS_DEBUG_COL (1 << 5) #define SCOLS_DEBUG_BUFF (1 << 6) #define SCOLS_DEBUG_GROUP (1 << 7) +#define SCOLS_DEBUG_FLTR (1 << 8) #define SCOLS_DEBUG_ALL 0xFFFF UL_DEBUG_DECLARE_MASK(libsmartcols); @@ -542,14 +543,13 @@ struct filter_expr { }; struct libscols_filter { + int refcount; + char *errmsg; 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 */ +/* required by parser */ struct filter_node *filter_new_param(struct libscols_filter *filter, enum filter_ptype type, void *data);