request_t *request; //!< The current request.
fr_dcursor_t cursor; //!< Used to track our place in the list
fr_pair_t *key; //!< local variable which contains the key
+ tmpl_t const *vpt; //!< pointer to the vpt
tmpl_dcursor_ctx_t cc; //!< tmpl cursor state
static int _free_unlang_frame_state_foreach(unlang_frame_state_foreach_t *state)
{
if (state->key) {
+ fr_pair_t *vp;
+
+ tmpl_dcursor_clear(&state->cc);
+
+ /*
+ * Now that we're done, the leaf entries can be changed again.
+ */
+ vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, state->request, state->vpt);
+ fr_assert(vp != NULL);
+
+ do {
+ if (fr_type_is_leaf(vp->vp_type)) fr_pair_clear_immutable(vp);
+
+ } while ((vp = fr_dcursor_next(&state->cursor)) != NULL);
tmpl_dcursor_clear(&state->cc);
+
} else {
request_data_get(state->request, FOREACH_REQUEST_DATA, state->depth);
}
vp = fr_dcursor_current(&state->cursor);
fr_assert(vp != NULL);
+ /*
+ * If we modified the value, copy it back to the original pair. Note that the copy does NOT
+ * check the "immutable" flag. That flag is for the people using unlang, not for the
+ * interpreter.
+ */
if (!fr_type_is_structural(vp->vp_type) && (vp->vp_type == state->key->vp_type)) {
fr_value_box_clear_value(&vp->data);
(void) fr_value_box_copy(vp, &vp->data, &state->key->data);
* We have a key variable, let's use that.
*/
if (gext->key) {
+ state->vpt = gext->vpt;
+
/*
* No matching attributes, we can't do anything.
*/
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.
*/
static inline CC_HINT(nonnull, always_inline)
void fr_pair_set_immutable(fr_pair_t *vp)
{
+ fr_assert(fr_type_is_leaf(vp->vp_type));
+
fr_value_box_set_immutable(&vp->data);
}
+static inline CC_HINT(nonnull, always_inline)
+void fr_pair_clear_immutable(fr_pair_t *vp)
+{
+ fr_assert(fr_type_is_leaf(vp->vp_type));
+
+ fr_value_box_clear_immutable(&vp->data);
+}
+
/* Lists */
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from);
--- /dev/null
+#
+# PRE: foreach-modify
+#
+
+&Tmp-Integer-0 := { 1, 3, 5, 11 }
+
+#
+# Try to delete one of the variables we're looping over. If the user could delete one,
+# then the underlying tmpl_dcursor would crash, as it didn't know about the deletion.
+#
+foreach uint32 self (&Tmp-Integer-0) {
+ &request -= &Tmp-Integer-0[1]
+ &self += 19
+}
+
+if (&Tmp-Integer-0[0] != 20) {
+ test_fail
+}
+
+if (&Tmp-Integer-0[1] != 22) {
+ test_fail
+}
+
+if (&Tmp-Integer-0[2] != 24) {
+ test_fail
+}
+
+if (&Tmp-Integer-0[3] != 30) {
+ test_fail
+}
+
+success