* will fix it up.
*/
token = cf_section_name2_quote(cs);
+ if (token != T_BARE_WORD) {
+ cf_log_err(cs, "Data being looped over in 'foreach' must be an attribute reference or dynamic expansion, not a string");
+ goto fail;
+ }
+
slen = tmpl_afrom_substr(g, &vpt,
&FR_SBUFF_IN(name2, strlen(name2)),
token,
*/
fr_assert(vpt);
- if (!tmpl_is_attr(vpt)) {
- cf_log_err(cs, "MUST use attribute or list reference (not %s) in 'foreach'",
- tmpl_type_to_str(vpt->type));
- goto fail;
- }
+ if (tmpl_is_attr(vpt)) {
+ if (tmpl_attr_tail_num(vpt) == NUM_UNSPEC) {
+ cf_log_warn(cs, "Attribute reference should be updated to use %s[*]", vpt->name);
+ tmpl_attr_rewrite_leaf_num(vpt, NUM_ALL);
+ }
- if (tmpl_attr_tail_num(vpt) == NUM_UNSPEC) {
- cf_log_warn(cs, "Attribute reference should be updated to use %s[*]", vpt->name);
- tmpl_attr_rewrite_leaf_num(vpt, NUM_ALL);
- }
+ if (tmpl_attr_tail_num(vpt) != NUM_ALL) {
+ cf_log_err(cs, "MUST NOT use instance selectors in 'foreach'");
+ goto fail;
+ }
- if (tmpl_attr_tail_num(vpt) != NUM_ALL) {
- cf_log_err(cs, "MUST NOT use instance selectors in 'foreach'");
+ } else if (!tmpl_contains_xlat(vpt)) {
+ cf_log_err(cs, "Invalid contents in 'foreach (...)', it must be an attribute reference or a dynamic expansion");
goto fail;
}
* If we have "type name", then define a local variable of that name.
*/
type_name = cf_section_argv(cs, 0); /* AFTER name1, name2 */
+
+ if (tmpl_is_xlat(vpt)) {
+ if (!type_name) {
+ /*
+ * @todo - find a way to get the data type.
+ */
+ cf_log_err(cs, "Dynamic expansions MUST specify a data type for the variable");
+ return NULL;
+ }
+
+ type = fr_table_value_by_str(fr_type_table, type_name, FR_TYPE_VOID);
+ if (!fr_type_is_leaf(type)) {
+ cf_log_err(cs, "Dynamic expansions MUST specify a non-structural data type for the variable");
+ return NULL;
+ }
+
+ goto get_name;
+ }
+
if (type_name) {
unlang_variable_t *var;
fr_dict_attr_t const *da = tmpl_attr_tail_da(vpt);
goto incompatible;
}
+ get_name:
variable_name = cf_section_argv(cs, 1);
/*
fr_pair_t *key; //!< local variable which contains the key
tmpl_t const *vpt; //!< pointer to the vpt
+ bool success; //!< for xlat expansion
+ fr_value_box_list_t list; //!< value box list for looping over xlats
+
tmpl_dcursor_ctx_t cc; //!< tmpl cursor state
///< we're iterating over.
return 0;
}
-static xlat_action_t unlang_foreach_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
- xlat_ctx_t const *xctx,
- request_t *request, UNUSED fr_value_box_list_t *in);
+static xlat_action_t unlang_foreach_xlat_func(TALLOC_CTX *ctx, fr_dcursor_t *out,
+ xlat_ctx_t const *xctx,
+ request_t *request, UNUSED fr_value_box_list_t *in);
-#define FOREACH_REQUEST_DATA (void *)unlang_foreach_xlat
+#define FOREACH_REQUEST_DATA (void *)unlang_foreach_xlat_func
/** Ensure request data is pulled out of the request if the frame is popped
*
if (state->key) {
fr_pair_t *vp;
+ if (tmpl_is_xlat(state->vpt)) return 0;
+
tmpl_dcursor_clear(&state->cc);
/*
return unlang_interpret_push_children(p_result, request, frame->result, UNLANG_NEXT_SIBLING);
}
+static unlang_action_t unlang_foreach_xlat_next(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
+{
+ unlang_frame_state_foreach_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t);
+ fr_value_box_t *box;
+
+next:
+ box = fr_dcursor_next(&state->cursor);
+ if (!box) {
+ *p_result = frame->result;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ fr_value_box_clear_value(&state->key->data);
+ if (fr_value_box_cast(state->key, &state->key->data, state->key->vp_type, state->key->da, box) < 0) {
+ RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pV", state->key->da->name, box);
+ goto next;
+ }
+
+ repeatable_set(frame);
+
+ /*
+ * Push the child, and yield for a later return.
+ */
+ return unlang_interpret_push_children(p_result, request, frame->result, UNLANG_NEXT_SIBLING);
+}
-static unlang_action_t unlang_foreach_next(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
+
+static unlang_action_t unlang_foreach_xlat_expanded(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
+{
+ unlang_frame_state_foreach_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t);
+ fr_value_box_t *box;
+
+ if (!state->success) {
+ RDEBUG("Failed expanding 'foreach' list");
+ *p_result = RLM_MODULE_FAIL;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ box = fr_dcursor_init(&state->cursor, fr_value_box_list_dlist_head(&state->list));
+ if (!box) {
+ done:
+ *p_result = RLM_MODULE_NOOP;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ fr_value_box_clear_value(&state->key->data);
+
+next:
+ if (fr_value_box_cast(state->key, &state->key->data, state->key->vp_type, state->key->da, box) < 0) {
+ RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pV", state->key->da->name, box);
+ box = fr_dcursor_next(&state->cursor);
+ if (!box) goto done;
+
+ goto next;
+ }
+
+ frame->process = unlang_foreach_xlat_next;
+ repeatable_set(frame);
+
+ /*
+ * Push the child, and yield for a later return.
+ */
+ return unlang_interpret_push_children(p_result, request, frame->result, UNLANG_NEXT_SIBLING);
+}
+
+
+/*
+ * Loop over an xlat expansion
+ */
+static unlang_action_t unlang_foreach_xlat_init(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame,
+ unlang_frame_state_foreach_t *state)
+{
+ fr_value_box_list_init(&state->list);
+
+ if (unlang_xlat_push(state, &state->success, &state->list, request, tmpl_xlat(state->vpt), false) < 0) {
+ REDEBUG("Failed starting expansion of %s", state->vpt->name);
+ *p_result = RLM_MODULE_FAIL;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ frame->process = unlang_foreach_xlat_expanded;
+ repeatable_set(frame);
+
+ return UNLANG_ACTION_PUSHED_CHILD;
+}
+
+
+static unlang_action_t unlang_foreach_attr_next(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
{
unlang_frame_state_foreach_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t);
fr_pair_t *vp;
return unlang_interpret_push_children(p_result, request, frame->result, UNLANG_NEXT_SIBLING);
}
+/*
+ * Loop over an attribute
+ */
+static unlang_action_t unlang_foreach_attr_init(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame,
+ unlang_frame_state_foreach_t *state)
+{
+ fr_pair_t *vp;
+
+ /*
+ * No matching attributes, we can't do anything.
+ */
+ vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, state->vpt);
+ if (!vp) {
+ *p_result = RLM_MODULE_NOOP;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ /*
+ * Before we loop over the variables, ensure that the user can't pull the rug out from
+ * under us.
+ */
+ do {
+ if (fr_type_is_leaf(vp->vp_type)) fr_pair_set_immutable(vp);
+
+ } while ((vp = fr_dcursor_next(&state->cursor)) != NULL);
+ tmpl_dcursor_clear(&state->cc);
+
+ vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, state->vpt);
+ fr_assert(vp != NULL);
+
+ if (vp->vp_type == FR_TYPE_GROUP) {
+ fr_assert(state->key->vp_type == FR_TYPE_GROUP);
+
+ if (fr_pair_list_copy(state->key, &state->key->vp_group, &vp->vp_group) < 0) {
+ REDEBUG("Failed copying members of %s", state->key->da->name);
+ *p_result = RLM_MODULE_FAIL;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ } else if (fr_type_is_structural(vp->vp_type)) {
+ fr_assert(state->key->vp_type == vp->vp_type);
+
+ if (unlang_foreach_pair_copy(state->key, vp, vp->da) < 0) {
+ REDEBUG("Failed copying children of %s", state->key->da->name);
+ *p_result = RLM_MODULE_FAIL;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ } else {
+ fr_value_box_clear_value(&state->key->data);
+ while (vp && (fr_value_box_cast(state->key, &state->key->data, state->key->vp_type, state->key->da, &vp->data) < 0)) {
+ RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pP", state->key->da->name, vp);
+ vp = fr_dcursor_next(&state->cursor);
+ }
+
+ /*
+ * Couldn't cast anything, the loop can't be run.
+ */
+ if (!vp) {
+ *p_result = RLM_MODULE_NOOP;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+ }
+
+ frame->process = unlang_foreach_attr_next;
+
+ repeatable_set(frame);
+
+ /*
+ * Push the child, and go process it.
+ */
+ return unlang_interpret_push_children(p_result, request, frame->result, UNLANG_NEXT_SIBLING);
+}
+
static unlang_action_t unlang_foreach(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
{
if (gext->key) {
state->vpt = gext->vpt;
- /*
- * No matching attributes, we can't do anything.
- */
- vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, gext->vpt);
- if (!vp) {
- *p_result = RLM_MODULE_NOOP;
- return UNLANG_ACTION_CALCULATE_RESULT;
- }
-
- /*
- * Before we loop over the variables, ensure that the user can't pull the rug out from
- * under us.
- */
- do {
- if (fr_type_is_leaf(vp->vp_type)) fr_pair_set_immutable(vp);
-
- } while ((vp = fr_dcursor_next(&state->cursor)) != NULL);
- tmpl_dcursor_clear(&state->cc);
-
- vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, gext->vpt);
- fr_assert(vp != NULL);
-
/*
* Create the local variable and populate its value.
*/
}
fr_assert(state->key != NULL);
- if (vp->vp_type == FR_TYPE_GROUP) {
- fr_assert(state->key->vp_type == FR_TYPE_GROUP);
-
- if (fr_pair_list_copy(state->key, &state->key->vp_group, &vp->vp_group) < 0) {
- REDEBUG("Failed copying members of %s", gext->key->name);
- *p_result = RLM_MODULE_FAIL;
- return UNLANG_ACTION_CALCULATE_RESULT;
- }
-
- } else if (fr_type_is_structural(vp->vp_type)) {
- fr_assert(state->key->vp_type == vp->vp_type);
-
- if (unlang_foreach_pair_copy(state->key, vp, vp->da) < 0) {
- REDEBUG("Failed copying children of %s", state->key->da->name);
- *p_result = RLM_MODULE_FAIL;
- return UNLANG_ACTION_CALCULATE_RESULT;
- }
-
- } else {
- fr_value_box_clear_value(&state->key->data);
- while (vp && (fr_value_box_cast(state->key, &state->key->data, state->key->vp_type, state->key->da, &vp->data) < 0)) {
- RDEBUG("Failed casting 'foreach' iteration variable '%s' from %pP", state->key->da->name, vp);
- vp = fr_dcursor_next(&state->cursor);
- }
-
- /*
- * Couldn't cast anything, the loop can't be run.
- */
- if (!vp) {
- *p_result = RLM_MODULE_NOOP;
- return UNLANG_ACTION_CALCULATE_RESULT;
- }
+ if (tmpl_is_attr(gext->vpt)) {
+ return unlang_foreach_attr_init(p_result, request, frame, state);
}
- frame->process = unlang_foreach_next;
-
- repeatable_set(frame);
+ fr_assert(tmpl_is_xlat(gext->vpt));
- /*
- * Push the child, and go process it.
- */
- return unlang_interpret_push_children(p_result, request, frame->result, UNLANG_NEXT_SIBLING);
+ return unlang_foreach_xlat_init(p_result, request, frame, state);
}
/*
*
* @ingroup xlat_functions
*/
-static xlat_action_t unlang_foreach_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
- xlat_ctx_t const *xctx,
- request_t *request, UNUSED fr_value_box_list_t *in)
+static xlat_action_t unlang_foreach_xlat_func(TALLOC_CTX *ctx, fr_dcursor_t *out,
+ xlat_ctx_t const *xctx,
+ request_t *request, UNUSED fr_value_box_list_t *in)
{
fr_pair_t *vp;
int const *inst = xctx->inst;
xlat_t *x;
x = xlat_func_register(ctx, xlat_foreach_names[i],
- unlang_foreach_xlat, FR_TYPE_VOID);
+ unlang_foreach_xlat_func, FR_TYPE_VOID);
fr_assert(x);
xlat_func_flags_set(x, XLAT_FUNC_FLAG_INTERNAL);
x->uctx = &xlat_foreach_inst[i];