RETURN_OK(slen);
}
+/** Perform calculations on multi-valued ops
+ *
+ */
+static size_t command_calc_nary(command_result_t *result, command_file_ctx_t *cc,
+ char *data, UNUSED size_t data_used, char *in, size_t inlen)
+{
+ fr_value_box_t *group, *a, *out;
+ size_t match_len;
+ fr_type_t type;
+ fr_token_t op;
+ char const *p, *value, *end;
+ size_t slen;
+
+ group = talloc_zero(cc->tmp_ctx, fr_value_box_t);
+ fr_value_box_init(group, FR_TYPE_GROUP, NULL, false);
+
+ p = in;
+ end = in + inlen;
+
+ /*
+ * Multi-valued operations
+ */
+ op = token2op[(uint8_t) p[0]];
+ if (op == T_INVALID) {
+ fr_strerror_printf("Unknown operator '%c'", p[0]);
+ RETURN_PARSE_ERROR(0);
+ }
+ p++;
+
+ while (p < end) {
+ fr_skip_whitespace(p);
+
+ a = talloc_zero(group, fr_value_box_t);
+
+ match_len = parse_typed_value(result, a, &value, p, end - p);
+ if (match_len == 0) return 0; /* errors have already been updated */
+
+ fr_value_box_list_insert_tail(&group->vb_group, a);
+
+ p += match_len;
+
+ if (strncmp(p, "->", 2) == 0) break;
+ }
+
+ out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
+
+ if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
+ p += 2;
+ fr_skip_whitespace(p);
+
+ type = fr_table_value_by_longest_prefix(&match_len, fr_type_table, p, end - p, FR_TYPE_MAX);
+ if (type == FR_TYPE_MAX) RETURN_PARSE_ERROR(0);
+
+
+ if (fr_value_calc_nary_op(cc->tmp_ctx, out, type, op, group) < 0) {
+ RETURN_OK_WITH_ERROR();
+ }
+
+ slen = fr_value_box_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), out, NULL);
+ if (slen <= 0) RETURN_OK_WITH_ERROR();
+
+ RETURN_OK(slen);
+}
+
/** Change the working directory
*
*/
.usage = "calc <type1> <value1> <operator> <type2> <value2> -> <output-type>",
.description = "Perform calculations on value boxes",
}},
+ { L("calc_nary "), &(command_entry_t){
+ .func = command_calc_nary,
+ .usage = "calc_nary op <type1> <value1> <type2> <value2> ... -> <output-type>",
+ .description = "Perform calculations on value boxes",
+ }},
{ L("cd "), &(command_entry_t){
.func = command_cd,
.usage = "cd <path>",
return 0;
}
+static void fr_value_box_init_zero(fr_value_box_t *vb, fr_type_t type)
+{
+ switch (type) {
+ case FR_TYPE_STRING:
+ fr_value_box_strdup_shallow(vb, NULL, "", false);
+ break;
+
+ case FR_TYPE_OCTETS:
+ fr_value_box_memdup_shallow(vb, NULL, (void const *) "", 0, false);
+ break;
+
+ default:
+ fr_value_box_init(vb, type, NULL, false);
+ break;
+ }
+}
+
static xlat_arg_parser_t const binary_op_xlat_args[] = {
{ .required = false, .type = FR_TYPE_VOID },
{ .required = false, .type = FR_TYPE_VOID },
}
a = &one;
-
- switch (b->type) {
- case FR_TYPE_STRING:
- fr_value_box_strdup_shallow(a, NULL, "", false);
- break;
-
- case FR_TYPE_OCTETS:
- fr_value_box_memdup_shallow(a, NULL, (void const *) "", 0, false);
- break;
-
- default:
- fr_value_box_init(a, b->type, NULL, false);
- break;
- }
+ fr_value_box_init_zero(a, b->type);
}
if (!b) {
}
b = &two;
-
- switch (a->type) {
- case FR_TYPE_STRING:
- fr_value_box_strdup_shallow(b, NULL, "", false);
- break;
-
- case FR_TYPE_OCTETS:
- fr_value_box_memdup_shallow(b, NULL, (void const *) "", 0, false);
- break;
-
- default:
- fr_value_box_init(b, a->type, NULL, false);
- break;
- }
+ fr_value_box_init_zero(b, a->type);
}
rcode = fr_value_calc_binary_op(dst, dst, default_type, a, op, b);
fr_value_box_list_t list;
} xlat_logical_rctx_t;
-static fr_slen_t xlat_expr_print_logical(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, fr_sbuff_escape_rules_t const *e_rules)
+static fr_slen_t xlat_expr_print_nary(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, fr_sbuff_escape_rules_t const *e_rules)
{
size_t at_in = fr_sbuff_used_total(out);
xlat_logical_inst_t *inst = instance;
if (unlikely((xlat = xlat_func_register(NULL, STRINGIFY(_name), xlat_func_ ## _func_name, FR_TYPE_VOID)) == NULL)) return -1; \
xlat_func_async_instantiate_set(xlat, xlat_instantiate_ ## _func_name, xlat_ ## _func_name ## _inst_t, NULL, NULL); \
xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
- xlat_func_print_set(xlat, xlat_expr_print_ ## _func_name); \
+ xlat_func_print_set(xlat, xlat_expr_print_nary); \
xlat_purify_func_set(xlat, xlat_expr_logical_purify); \
xlat->token = _op; \
} while (0)
[T_LOR] = true,
};
+/*
+ * These operators can take multiple arguments.
+ */
+static const bool multivalue_ops[T_TOKEN_LAST] = {
+ [T_LAND] = true,
+ [T_LOR] = true,
+};
+
/*
* Allow for BEDMAS ordering. Gross ordering is first number,
* fine ordering is second number. Unused operators are assigned as zero.
fr_sbuff_set(&our_in, &m_rhs);
FR_SBUFF_ERROR_RETURN(&our_in);
}
+ }
+ if (multivalue_ops[op]) {
if ((lhs->type == XLAT_FUNC) && (lhs->call.func->token == op)) {
xlat_func_append_arg(lhs, rhs, cond);
goto redo;
}
- if (reparse_rcode(head, &lhs, true) < 0) goto fail_lhs;
+ if (logical_ops[op]) if (reparse_rcode(head, &lhs, true) < 0) goto fail_lhs;
goto purify;
}
return handle_result(hint, op, rcode);
}
+/** Calculate DST = OP { A, B, C, ... }
+ *
+ * The result is written to DST only *after* it has been calculated.
+ * So it's safe to pass DST as one of the inputs. DST should already
+ * exist.
+ */
+int fr_value_calc_nary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t type, fr_token_t op, fr_value_box_t const *group)
+{
+ fr_value_box_t out, *vb;
+ fr_binary_op_t calc;
+
+ if (group->type != FR_TYPE_GROUP) {
+ fr_strerror_const("Invalid type passed to multivalue calculation");
+ return -1;
+ }
+
+ if (fr_type_is_structural(type)) {
+ invalid_type:
+ fr_strerror_printf("Invalid operation %s for data type %s", fr_tokens[op], fr_type_to_str(type));
+ return -1;
+ }
+
+ if (type == FR_TYPE_STRING) {
+ fr_sbuff_t *sbuff;
+ bool secret = false;
+ bool tainted = false;
+
+ if (op != T_ADD) goto invalid_type;
+
+ FR_SBUFF_TALLOC_THREAD_LOCAL(&sbuff, 1024, (1 << 16));
+
+ if (fr_value_box_list_concat_as_string(&tainted, &secret, sbuff, UNCONST(fr_value_box_list_t *, &group->vb_group), NULL, 0, NULL, FR_VALUE_BOX_LIST_NONE, false) < 0) return -1;
+
+ if (fr_value_box_bstrndup(ctx, dst, NULL, fr_sbuff_start(sbuff), fr_sbuff_used(sbuff), tainted) < 0) return -1;
+
+ fr_value_box_set_secret(dst, secret);
+
+ return 0;
+ }
+
+ if (type == FR_TYPE_OCTETS) {
+ fr_dbuff_t *dbuff;
+ bool secret = false;
+ bool tainted = false;
+
+ if (op != T_ADD) goto invalid_type;
+
+ FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, (1 << 16));
+
+ if (fr_value_box_list_concat_as_octets(&tainted, &secret, dbuff, UNCONST(fr_value_box_list_t *, &group->vb_group), NULL, 0, FR_VALUE_BOX_LIST_NONE, false) < 0) return -1;
+
+ if (fr_value_box_memdup(ctx, dst, NULL, fr_dbuff_start(dbuff), fr_dbuff_used(dbuff), tainted) < 0) return -1;
+
+ fr_value_box_set_secret(dst, secret);
+
+ return 0;
+ }
+
+ /*
+ * Can't add or multiply booleans.
+ */
+ if ((type == FR_TYPE_BOOL) && !((op == T_AND) || (op == T_OR) || (op == T_XOR))) goto unsupported;
+
+ switch (op) {
+ case T_ADD:
+ case T_MUL:
+ case T_AND:
+ case T_OR:
+ case T_XOR:
+ break;
+
+ default:
+ goto invalid_type;
+ }
+
+ /*
+ * Strings and octets are different.
+ */
+ if (!fr_type_is_numeric(type)) {
+ unsupported:
+ fr_strerror_printf("Not yet supported operation %s for data type %s", fr_tokens[op], fr_type_to_str(type));
+ return -1;
+ }
+
+ switch (type) {
+ case FR_TYPE_UINT8:
+ case FR_TYPE_UINT16:
+ case FR_TYPE_UINT32:
+ case FR_TYPE_UINT64:
+ calc = calc_uint64;
+ break;
+
+ case FR_TYPE_INT8:
+ case FR_TYPE_INT16:
+ case FR_TYPE_INT32:
+ case FR_TYPE_INT64:
+ calc = calc_int64;
+ break;
+
+ case FR_TYPE_FLOAT32:
+ calc = calc_float32;
+ break;
+
+ case FR_TYPE_FLOAT64:
+ calc = calc_float64;
+ break;
+
+ default:
+ goto unsupported;
+ }
+
+ vb = fr_value_box_list_head(&group->vb_group);
+ if (!vb) {
+ fr_strerror_printf("Empty input is invalid");
+ return -1;
+ }
+
+ if (fr_value_box_cast(ctx, &out, type, NULL, vb) < 0) return -1;
+
+ while ((vb = fr_value_box_list_next(&group->vb_group, vb)) != NULL) {
+ int rcode;
+ fr_value_box_t box;
+
+ if (vb->type == type) {
+ rcode = calc(ctx, &out, &out, op, vb);
+ if (rcode < 0) return rcode;
+
+ } else {
+ if (fr_value_box_cast(ctx, &box, type, NULL, vb) < 0) return -1;
+
+ rcode = calc(ctx, &out, &out, op, &box);
+ if (rcode < 0) return rcode;
+ }
+ }
+
+ return fr_value_box_copy(ctx, dst, &out);
+}
+
+
#define T(_x) [T_OP_ ## _x ## _EQ] = T_ ## _x
static const fr_token_t assignment2op[T_TOKEN_LAST] = {
int fr_value_calc_unary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_token_t op, fr_value_box_t const *src) CC_HINT(nonnull(1));
+int fr_value_calc_nary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t type, fr_token_t op, fr_value_box_t const *group) CC_HINT(nonnull(2,5));
+
int fr_value_calc_list_op(TALLOC_CTX *ctx, fr_value_box_t *box, fr_token_t op, fr_value_box_list_t const *list);
int fr_value_calc_list_cmp(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_list_t const *list1, fr_token_t op, fr_value_box_list_t const *list2) CC_HINT(nonnull);
--- /dev/null
+#
+# Calculate multi-value operations
+#
+calc_nary + uint8 1 uint16 2 uint32 3 -> uint32
+match 6
+
+calc_nary & uint8 255 uint16 192 uint32 127 -> uint8
+match 64
+
+calc_nary + string "foo " string "bar " string "baz" -> string
+match foo bar baz
+
+calc_nary + string "foo " octets 0x7e4a5a string "baz" -> string
+match foo ~JZbaz
+
+calc_nary + ipaddr 127.0.0.1 uint32 45 string "baz" -> string
+match 127.0.0.145baz
+
+calc_nary + octets 0xabcdef -> octets
+match 0xabcdef
+
+calc_nary + octets 0xabcdef octets 0x11223344 -> octets
+match 0xabcdef11223344
+
+calc_nary + octets 0xabcdef string "a" -> octets
+match 0xabcdef61
+
+calc_nary + octets 0xabcdef ipaddr 127.0.0.1 -> octets
+match 0xabcdef7f000001
+
+calc_nary + octets 0xabcdef ipaddr 127.0.0.1 string "foo" -> octets
+match 0xabcdef7f000001666f6f
+
+count
+match 20