|
22 | 22 | included file COSL.txt.
|
23 | 23 | */
|
24 | 24 |
|
| 25 | +#include <limits.h> |
25 | 26 | #include <platform.h>
|
26 | 27 | #include <evalfunction.h>
|
27 | 28 |
|
|
35 | 36 | #include <files_names.h>
|
36 | 37 | #include <files_interfaces.h>
|
37 | 38 | #include <hash.h>
|
| 39 | +#include <stddef.h> |
| 40 | +#include <stdint.h> |
38 | 41 | #include <vars.h>
|
39 | 42 | #include <addr_lib.h>
|
40 | 43 | #include <syntax.h>
|
@@ -7777,6 +7780,190 @@ static int JsonPrimitiveComparator(JsonElement const *left_obj,
|
7777 | 7780 | return StringSafeCompare(left, right);
|
7778 | 7781 | }
|
7779 | 7782 |
|
| 7783 | +static bool ClassFilterDataArrayOfArrays( |
| 7784 | + EvalContext *ctx, |
| 7785 | + const char *fn_name, |
| 7786 | + JsonElement *json_array, |
| 7787 | + const char *class_expr_index, |
| 7788 | + bool *remove) |
| 7789 | +{ |
| 7790 | + errno = 0; /* to detect error */ |
| 7791 | + assert(SIZE_MAX >= ULONG_MAX); /* make sure returned value can fit in size_t */ |
| 7792 | + char *endptr; |
| 7793 | + size_t index = strtoul(class_expr_index, &endptr, 10); |
| 7794 | + if (!StringEqual(endptr, "")) /* check that the whole string was consumed */ |
| 7795 | + { |
| 7796 | + Log(LOG_LEVEL_VERBOSE, |
| 7797 | + "Function %s(): Bad class expression index '%s': Not a valid integer", |
| 7798 | + fn_name, class_expr_index); |
| 7799 | + return false; |
| 7800 | + } |
| 7801 | + if (errno != 0) |
| 7802 | + { |
| 7803 | + Log(LOG_LEVEL_VERBOSE, |
| 7804 | + "Function %s(): Bad class expression index '%s': %s", |
| 7805 | + fn_name, class_expr_index, GetErrorStr()); |
| 7806 | + return false; |
| 7807 | + } |
| 7808 | + |
| 7809 | + size_t length = JsonLength(json_array); |
| 7810 | + if (index >= length) |
| 7811 | + { |
| 7812 | + Log(LOG_LEVEL_VERBOSE, |
| 7813 | + "Function %s(): Bad class expression index '%s': Index out of bounds (%zu >= %zu)", |
| 7814 | + fn_name, class_expr_index, index, length); |
| 7815 | + return false; |
| 7816 | + } |
| 7817 | + |
| 7818 | + JsonElement *json_child = JsonArrayGet(json_array, index); |
| 7819 | + if (JsonGetType(json_child) != JSON_TYPE_STRING) |
| 7820 | + { |
| 7821 | + Log(LOG_LEVEL_VERBOSE, |
| 7822 | + "Function %s(): Bad class expression at index '%zu': Expected type string", |
| 7823 | + fn_name, index); |
| 7824 | + return false; |
| 7825 | + } |
| 7826 | + |
| 7827 | + const char *class_expr = JsonPrimitiveGetAsString(json_child); |
| 7828 | + assert(class_expr != NULL); |
| 7829 | + |
| 7830 | + *remove = !IsDefinedClass(ctx, class_expr); |
| 7831 | + return true; |
| 7832 | +} |
| 7833 | + |
| 7834 | +static bool ClassFilterDataArrayOfObjects( |
| 7835 | + EvalContext *ctx, |
| 7836 | + const char *fn_name, |
| 7837 | + JsonElement *json_object, |
| 7838 | + const char *class_expr_key, |
| 7839 | + bool *remove) |
| 7840 | +{ |
| 7841 | + JsonElement *json_child = JsonObjectGet(json_object, class_expr_key); |
| 7842 | + if (json_child == NULL) |
| 7843 | + { |
| 7844 | + Log(LOG_LEVEL_VERBOSE, |
| 7845 | + "Function %s(): Bad class expression key '%s': Key not found", |
| 7846 | + fn_name, class_expr_key); |
| 7847 | + return false; |
| 7848 | + } |
| 7849 | + |
| 7850 | + if (JsonGetType(json_child) != JSON_TYPE_STRING) |
| 7851 | + { |
| 7852 | + Log(LOG_LEVEL_VERBOSE, |
| 7853 | + "Function %s(): Bad class expression at key '%s': Expected type string", |
| 7854 | + fn_name, class_expr_key); |
| 7855 | + return false; |
| 7856 | + } |
| 7857 | + |
| 7858 | + const char *class_expr = JsonPrimitiveGetAsString(json_child); |
| 7859 | + assert(class_expr != NULL); |
| 7860 | + |
| 7861 | + *remove = !IsDefinedClass(ctx, class_expr); |
| 7862 | + return true; |
| 7863 | +} |
| 7864 | + |
| 7865 | +static bool ClassFilterDataArray( |
| 7866 | + EvalContext *ctx, |
| 7867 | + const char *fn_name, |
| 7868 | + const char *data_structure, |
| 7869 | + const char *key_or_index, |
| 7870 | + JsonElement *child, |
| 7871 | + bool *remove) |
| 7872 | +{ |
| 7873 | + switch (JsonGetType(child)) |
| 7874 | + { |
| 7875 | + case JSON_TYPE_ARRAY: |
| 7876 | + if (StringEqual(data_structure, "auto") || |
| 7877 | + StringEqual(data_structure, "array_of_arrays")) |
| 7878 | + { |
| 7879 | + return ClassFilterDataArrayOfArrays( |
| 7880 | + ctx, fn_name, child, key_or_index, remove); |
| 7881 | + } |
| 7882 | + Log(LOG_LEVEL_VERBOSE, |
| 7883 | + "Function %s(): Expected child element to be of container type array", |
| 7884 | + fn_name); |
| 7885 | + break; |
| 7886 | + |
| 7887 | + case JSON_TYPE_OBJECT: |
| 7888 | + if (StringEqual(data_structure, "auto") || |
| 7889 | + StringEqual(data_structure, "array_of_objects")) |
| 7890 | + { |
| 7891 | + return ClassFilterDataArrayOfObjects( |
| 7892 | + ctx, fn_name, child, key_or_index, remove); |
| 7893 | + } |
| 7894 | + Log(LOG_LEVEL_VERBOSE, |
| 7895 | + "Function %s(): Expected child element to be of container type object", |
| 7896 | + fn_name); |
| 7897 | + break; |
| 7898 | + |
| 7899 | + default: |
| 7900 | + Log(LOG_LEVEL_VERBOSE, |
| 7901 | + "Function %s(): Expected child element to be of container type", |
| 7902 | + fn_name); |
| 7903 | + break; |
| 7904 | + } |
| 7905 | + |
| 7906 | + return false; |
| 7907 | +} |
| 7908 | + |
| 7909 | +static FnCallResult FnCallClassFilterData( |
| 7910 | + EvalContext *ctx, |
| 7911 | + ARG_UNUSED Policy const *policy, |
| 7912 | + FnCall const *fp, |
| 7913 | + Rlist const *args) |
| 7914 | +{ |
| 7915 | + assert(ctx != NULL); |
| 7916 | + assert(fp != NULL); |
| 7917 | + assert(args != NULL); |
| 7918 | + assert(args->next != NULL); |
| 7919 | + assert(args->next->next != NULL); |
| 7920 | + |
| 7921 | + bool allocated = false; |
| 7922 | + JsonElement *parent = VarNameOrInlineToJson(ctx, fp, args, false, &allocated); |
| 7923 | + if (parent == NULL) |
| 7924 | + { |
| 7925 | + Log(LOG_LEVEL_VERBOSE, |
| 7926 | + "Function %s(): Expected parent element to be of container type array", |
| 7927 | + fp->name); |
| 7928 | + return FnFailure(); |
| 7929 | + } |
| 7930 | + |
| 7931 | + /* Currently only parent type array is supported */ |
| 7932 | + if (JsonGetType(parent) != JSON_TYPE_ARRAY) |
| 7933 | + { |
| 7934 | + JsonDestroyMaybe(parent, allocated); |
| 7935 | + return FnFailure(); |
| 7936 | + } |
| 7937 | + assert(allocated); /* Non-primitives are always allocated */ |
| 7938 | + |
| 7939 | + const char *data_structure = RlistScalarValue(args->next); |
| 7940 | + const char *key_or_index = RlistScalarValue(args->next->next); |
| 7941 | + |
| 7942 | + /* Iterate through array backwards so we can avoid having to compute index |
| 7943 | + * offsets for each removed element */ |
| 7944 | + for (size_t i = JsonLength(parent); i > 0; i--) |
| 7945 | + { |
| 7946 | + size_t index = i - 1; |
| 7947 | + JsonElement *child = JsonArrayGet(parent, index); |
| 7948 | + assert(child != NULL); |
| 7949 | + |
| 7950 | + bool remove; |
| 7951 | + if (!ClassFilterDataArray(ctx, fp->name, data_structure, key_or_index, child, &remove)) |
| 7952 | + { |
| 7953 | + /* Error is already logged */ |
| 7954 | + JsonDestroy(parent); |
| 7955 | + return FnFailure(); |
| 7956 | + } |
| 7957 | + |
| 7958 | + if (remove) |
| 7959 | + { |
| 7960 | + JsonArrayRemoveRange(parent, index, index); |
| 7961 | + } |
| 7962 | + } |
| 7963 | + |
| 7964 | + return FnReturnContainerNoCopy(parent); |
| 7965 | +} |
| 7966 | + |
7780 | 7967 | static FnCallResult FnCallClassFilterCsv(EvalContext *ctx,
|
7781 | 7968 | ARG_UNUSED Policy const *policy,
|
7782 | 7969 | FnCall const *fp,
|
@@ -10371,6 +10558,14 @@ static const FnCallArg VALIDJSON_ARGS[] =
|
10371 | 10558 | {NULL, CF_DATA_TYPE_NONE, NULL}
|
10372 | 10559 | };
|
10373 | 10560 |
|
| 10561 | +static const FnCallArg CLASSFILTERDATA_ARGS[] = |
| 10562 | +{ |
| 10563 | + {CF_ANYSTRING, CF_DATA_TYPE_STRING, "CFEngine variable identifier or inline JSON"}, |
| 10564 | + {"array_of_arrays,array_of_objects,auto", CF_DATA_TYPE_OPTION, "Specify type of data structure"}, |
| 10565 | + {CF_ANYSTRING, CF_DATA_TYPE_STRING, "Key or index of class expressions"}, |
| 10566 | + {NULL, CF_DATA_TYPE_NONE, NULL} |
| 10567 | +}; |
| 10568 | + |
10374 | 10569 | static const FnCallArg CLASSFILTERCSV_ARGS[] =
|
10375 | 10570 | {
|
10376 | 10571 | {CF_ABSPATHRANGE, CF_DATA_TYPE_STRING, "File name"},
|
@@ -10907,6 +11102,8 @@ const FnCallType CF_FNCALL_TYPES[] =
|
10907 | 11102 | FNCALL_OPTION_VARARG, FNCALL_CATEGORY_UTILS, SYNTAX_STATUS_NORMAL),
|
10908 | 11103 | FnCallTypeNew("classesmatching", CF_DATA_TYPE_STRING_LIST, CLASSMATCH_ARGS, &FnCallClassesMatching, "List the defined classes matching regex arg1 and tag regexes arg2,arg3,...",
|
10909 | 11104 | FNCALL_OPTION_VARARG, FNCALL_CATEGORY_UTILS, SYNTAX_STATUS_NORMAL),
|
| 11105 | + FnCallTypeNew("classfilterdata", CF_DATA_TYPE_CONTAINER, CLASSFILTERDATA_ARGS, &FnCallClassFilterData, "Filter data container by defined classes", |
| 11106 | + FNCALL_OPTION_VARARG, FNCALL_CATEGORY_IO, SYNTAX_STATUS_NORMAL), |
10910 | 11107 | FnCallTypeNew("classfiltercsv", CF_DATA_TYPE_CONTAINER, CLASSFILTERCSV_ARGS, &FnCallClassFilterCsv, "Parse a CSV file and create data container filtered by defined classes",
|
10911 | 11108 | FNCALL_OPTION_VARARG, FNCALL_CATEGORY_IO, SYNTAX_STATUS_NORMAL),
|
10912 | 11109 | FnCallTypeNew("countclassesmatching", CF_DATA_TYPE_INT, CLASSMATCH_ARGS, &FnCallClassesMatching, "Count the number of defined classes matching regex arg1",
|
|
0 commit comments