From fd0fb8e061b6f85785db0469b5521b6b80588d87 Mon Sep 17 00:00:00 2001 From: Attila Szakacs Date: Thu, 16 May 2024 10:14:47 +0200 Subject: [PATCH] filterx: add unset_empties() Signed-off-by: Attila Szakacs --- lib/filterx/CMakeLists.txt | 2 + lib/filterx/Makefile.am | 2 + lib/filterx/filterx-globals.c | 2 + lib/filterx/func-unset-empties.c | 276 +++++++++++++++++++++++++++++++ lib/filterx/func-unset-empties.h | 32 ++++ 5 files changed, 314 insertions(+) create mode 100644 lib/filterx/func-unset-empties.c create mode 100644 lib/filterx/func-unset-empties.h diff --git a/lib/filterx/CMakeLists.txt b/lib/filterx/CMakeLists.txt index e28abdeb80..d236bec357 100644 --- a/lib/filterx/CMakeLists.txt +++ b/lib/filterx/CMakeLists.txt @@ -39,6 +39,7 @@ set(FILTERX_HEADERS filterx/func-istype.h filterx/func-len.h filterx/func-vars.h + filterx/func-unset-empties.h filterx/expr-plus.h PARENT_SCOPE ) @@ -84,6 +85,7 @@ set(FILTERX_SOURCES filterx/func-istype.c filterx/func-len.c filterx/func-vars.c + filterx/func-unset-empties.c filterx/expr-plus.c filterx/filterx-private.c PARENT_SCOPE diff --git a/lib/filterx/Makefile.am b/lib/filterx/Makefile.am index 62acf92637..d0cc66ed4c 100644 --- a/lib/filterx/Makefile.am +++ b/lib/filterx/Makefile.am @@ -41,6 +41,7 @@ filterxinclude_HEADERS = \ lib/filterx/func-istype.h \ lib/filterx/func-len.h \ lib/filterx/func-vars.h \ + lib/filterx/func-unset-empties.h \ lib/filterx/filterx-private.h @@ -86,6 +87,7 @@ filterx_sources = \ lib/filterx/func-istype.c \ lib/filterx/func-len.c \ lib/filterx/func-vars.c \ + lib/filterx/func-unset-empties.c \ lib/filterx/filterx-private.c \ lib/filterx/filterx-grammar.y diff --git a/lib/filterx/filterx-globals.c b/lib/filterx/filterx-globals.c index 4db93b4bb2..506cf722da 100644 --- a/lib/filterx/filterx-globals.c +++ b/lib/filterx/filterx-globals.c @@ -33,6 +33,7 @@ #include "filterx/func-istype.h" #include "filterx/func-len.h" #include "filterx/func-vars.h" +#include "filterx/func-unset-empties.h" static GHashTable *filterx_builtin_simple_functions = NULL; static GHashTable *filterx_builtin_function_ctors = NULL; @@ -110,6 +111,7 @@ _ctors_init(void) filterx_builtin_function_ctors_init_private(&filterx_builtin_function_ctors); g_assert(filterx_builtin_function_ctor_register("strptime", filterx_function_strptime_new)); g_assert(filterx_builtin_function_ctor_register("istype", filterx_function_istype_new)); + g_assert(filterx_builtin_function_ctor_register("unset_empties", filterx_function_unset_empties_new)); } static void diff --git a/lib/filterx/func-unset-empties.c b/lib/filterx/func-unset-empties.c new file mode 100644 index 0000000000..9131b7ce10 --- /dev/null +++ b/lib/filterx/func-unset-empties.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2024 Attila Szakacs + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As an additional exemption you are allowed to compile & link against the + * OpenSSL libraries as published by the OpenSSL project. See the file + * COPYING for details. + * + */ + +#include "filterx/func-unset-empties.h" +#include "filterx/object-string.h" +#include "filterx/object-primitive.h" +#include "filterx/object-null.h" +#include "filterx/object-dict-interface.h" +#include "filterx/object-list-interface.h" +#include "filterx/filterx-eval.h" +#include "filterx/filterx-globals.h" + +#define FILTERX_FUNC_UNSET_EMPTIES_USAGE "Usage: unset_empties(object, recursive=true)" + +typedef struct FilterXFunctionUnsetEmpties_ +{ + FilterXFunction super; + FilterXExpr *object_expr; + gboolean recursive; +} FilterXFunctionUnsetEmpties; + +static gboolean _process_dict(FilterXFunctionUnsetEmpties *self, FilterXObject *obj); +static gboolean _process_list(FilterXFunctionUnsetEmpties *self, FilterXObject *obj); + +static gboolean +_should_unset(FilterXFunctionUnsetEmpties *self, FilterXObject *obj) +{ + if (filterx_object_is_type(obj, &FILTERX_TYPE_NAME(string))) + { + gsize len; + const gchar *value = filterx_string_get_value(obj, &len); + return len == 0 || + strcasecmp(value, "n/a") == 0 || + strcmp(value, "-") == 0; + } + + if (filterx_object_is_type(obj, &FILTERX_TYPE_NAME(null))) + { + return TRUE; + } + + if (filterx_object_is_type(obj, &FILTERX_TYPE_NAME(dict)) || + filterx_object_is_type(obj, &FILTERX_TYPE_NAME(list))) + { + guint64 len; + filterx_object_len(obj, &len); + return len == 0; + } + + return FALSE; +} + +/* Also unsets inner dicts' and lists' values is recursive is set. */ +static gboolean +_add_key_to_unset_list_if_needed(FilterXObject *key, FilterXObject *value, gpointer user_data) +{ + FilterXFunctionUnsetEmpties *self = ((gpointer *) user_data)[0]; + GList **keys_to_unset = ((gpointer *) user_data)[1]; + + if (self->recursive) + { + if (filterx_object_is_type(value, &FILTERX_TYPE_NAME(dict)) && !_process_dict(self, value)) + return FALSE; + if (filterx_object_is_type(value, &FILTERX_TYPE_NAME(list)) && !_process_list(self, value)) + return FALSE; + } + + if (!_should_unset(self, value)) + return TRUE; + + *keys_to_unset = g_list_append(*keys_to_unset, filterx_object_ref(key)); + return TRUE; +} + +static gboolean +_process_dict(FilterXFunctionUnsetEmpties *self, FilterXObject *obj) +{ + GList *keys_to_unset = NULL; + gpointer user_data[] = { self, &keys_to_unset }; + gboolean success = filterx_dict_iter(obj, _add_key_to_unset_list_if_needed, user_data); + + for (GList *elem = keys_to_unset; elem && success; elem = elem->next) + { + FilterXObject *key = (FilterXObject *) elem->data; + if (!filterx_object_unset_key(obj, key)) + success = FALSE; + } + + g_list_free_full(keys_to_unset, (GDestroyNotify) filterx_object_unref); + return success; +} + +/* Takes reference of obj. */ +static FilterXObject * +_eval_on_dict(FilterXFunctionUnsetEmpties *self, FilterXObject *obj) +{ + gboolean success = _process_dict(self, obj); + filterx_object_unref(obj); + return success ? filterx_boolean_new(TRUE) : NULL; +} + +static gboolean +_process_list(FilterXFunctionUnsetEmpties *self, FilterXObject *obj) +{ + guint64 len; + filterx_object_len(obj, &len); + if (len == 0) + return TRUE; + + for (gint64 i = ((gint64) len) - 1; i >= 0; i--) + { + FilterXObject *elem = filterx_list_get_subscript(obj, i); + + if (self->recursive) + { + if (filterx_object_is_type(elem, &FILTERX_TYPE_NAME(dict)) && !_process_dict(self, elem)) + { + filterx_object_unref(elem); + return FALSE; + } + if (filterx_object_is_type(elem, &FILTERX_TYPE_NAME(list)) && !_process_list(self, elem)) + { + filterx_object_unref(elem); + return FALSE; + } + } + + if (_should_unset(self, elem)) + { + if (!filterx_list_unset_index(obj, i)) + { + filterx_object_unref(elem); + return FALSE; + } + } + + filterx_object_unref(elem); + } + + return TRUE; +} + +/* Takes reference of obj. */ +static FilterXObject * +_eval_on_list(FilterXFunctionUnsetEmpties *self, FilterXObject *obj) +{ + gboolean success = _process_list(self, obj); + filterx_object_unref(obj); + return success ? filterx_boolean_new(TRUE) : NULL; +} + +static FilterXObject * +_eval(FilterXExpr *s) +{ + FilterXFunctionUnsetEmpties *self = (FilterXFunctionUnsetEmpties *) s; + + FilterXObject *obj = filterx_expr_eval(self->object_expr); + if (!obj) + { + filterx_eval_push_error("Failed to evaluate first argument. " FILTERX_FUNC_UNSET_EMPTIES_USAGE, s, NULL); + return NULL; + } + + if (filterx_object_is_type(obj, &FILTERX_TYPE_NAME(dict))) + return _eval_on_dict(self, obj); + + if (filterx_object_is_type(obj, &FILTERX_TYPE_NAME(list))) + return _eval_on_list(self, obj); + + filterx_eval_push_error("Object must be dict or list. " FILTERX_FUNC_UNSET_EMPTIES_USAGE, s, obj); + filterx_object_unref(obj); + return NULL; +} + +static void +_free(FilterXExpr *s) +{ + FilterXFunctionUnsetEmpties *self = (FilterXFunctionUnsetEmpties *) s; + filterx_expr_unref(self->object_expr); + filterx_function_free_method(&self->super); +} + +static FilterXExpr * +_extract_object_expr(FilterXFunctionArgs *args, GError **error) +{ + FilterXExpr *object_expr = filterx_function_args_get_expr(args, 0); + if (!object_expr) + { + g_set_error(error, FILTERX_FUNCTION_ERROR, FILTERX_FUNCTION_ERROR_CTOR_FAIL, + "argument must be set: object. " FILTERX_FUNC_UNSET_EMPTIES_USAGE); + return NULL; + } + + return object_expr; +} + +static gboolean +_extract_optional_args(FilterXFunctionUnsetEmpties *self, FilterXFunctionArgs *args, GError **error) +{ + gboolean exists, eval_error; + gboolean value = filterx_function_args_get_named_literal_boolean(args, "recursive", &exists, &eval_error); + if (!exists) + return TRUE; + + if (eval_error) + { + g_set_error(error, FILTERX_FUNCTION_ERROR, FILTERX_FUNCTION_ERROR_CTOR_FAIL, + "recursive argument must be boolean literal. " FILTERX_FUNC_UNSET_EMPTIES_USAGE); + return FALSE; + } + + self->recursive = value; + return TRUE; +} + +static gboolean +_extract_args(FilterXFunctionUnsetEmpties *self, FilterXFunctionArgs *args, GError **error) +{ + if (filterx_function_args_len(args) != 1) + { + g_set_error(error, FILTERX_FUNCTION_ERROR, FILTERX_FUNCTION_ERROR_CTOR_FAIL, + "invalid number of arguments. " FILTERX_FUNC_UNSET_EMPTIES_USAGE); + return FALSE; + } + + self->object_expr = _extract_object_expr(args, error); + if (!self->object_expr) + return FALSE; + + if (!_extract_optional_args(self, args, error)) + return FALSE; + + return TRUE; +} + +FilterXFunction * +filterx_function_unset_empties_new(const gchar *function_name, FilterXFunctionArgs *args, GError **error) +{ + FilterXFunctionUnsetEmpties *self = g_new0(FilterXFunctionUnsetEmpties, 1); + filterx_function_init_instance(&self->super, function_name); + self->super.super.eval = _eval; + self->super.super.free_fn = _free; + + self->recursive = TRUE; + + if (!_extract_args(self, args, error)) + goto error; + + filterx_function_args_free(args); + return &self->super; + +error: + filterx_function_args_free(args); + filterx_expr_unref(&self->super.super); + return NULL; +} diff --git a/lib/filterx/func-unset-empties.h b/lib/filterx/func-unset-empties.h new file mode 100644 index 0000000000..5ca8253b1c --- /dev/null +++ b/lib/filterx/func-unset-empties.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Attila Szakacs + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As an additional exemption you are allowed to compile & link against the + * OpenSSL libraries as published by the OpenSSL project. See the file + * COPYING for details. + * + */ + +#ifndef FILTERX_FUNC_UNSET_EMPTIES_H_INCLUDED +#define FILTERX_FUNC_UNSET_EMPTIES_H_INCLUDED + +#include "filterx/expr-function.h" + +FilterXFunction *filterx_function_unset_empties_new(const gchar *function_name, FilterXFunctionArgs *args, + GError **error); + +#endif