From 00ba4171f4ff1cf5accecc4ffa7052af9365b131 Mon Sep 17 00:00:00 2001 From: "Alan T. DeKok" Date: Sun, 12 Dec 2021 13:52:50 -0500 Subject: [PATCH] allow list -= attr for now, only one attr on the RHS. But we hackily allow &attr[0] or &attr[*] --- src/lib/unlang/edit.c | 89 +++++++++++++++++++++++++++-- src/tests/keywords/edit-list-remove | 70 +++++++++++++++++++++++ 2 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 src/tests/keywords/edit-list-remove diff --git a/src/lib/unlang/edit.c b/src/lib/unlang/edit.c index ca629acff8..a3e8b2f142 100644 --- a/src/lib/unlang/edit.c +++ b/src/lib/unlang/edit.c @@ -112,7 +112,6 @@ static int templatize_lhs(TALLOC_CTX *ctx, edit_result_t *out, request_t *reques */ static int templatize_rhs(TALLOC_CTX *ctx, edit_result_t *out, fr_pair_t const *lhs, request_t *request) { - ssize_t slen; fr_type_t type = lhs->vp_type; fr_value_box_t *box = fr_dlist_head(&out->result); @@ -194,6 +193,64 @@ static int template_realize(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_ return -1; } +/** Remove VPs for laziness + * + */ +static int remove_vps(request_t *request, unlang_frame_state_edit_t *state, map_t const *map) +{ + fr_pair_t *vp, *next, *last; + fr_pair_list_t *list; + fr_dict_attr_t const *da; + int16_t num, count; + + fr_assert(map->op == T_OP_SUB_EQ); + fr_assert(tmpl_is_attr(state->rhs.vpt)); + fr_assert(state->lhs.vp != NULL); + fr_assert(fr_type_is_structural(state->lhs.vp->vp_type)); + + list = &state->lhs.vp->vp_group; + da = tmpl_da(state->rhs.vpt); + + RDEBUG2("%s %s %s", state->lhs.vpt->name, fr_tokens[map->op], state->rhs.vpt->name); + + num = tmpl_num(state->rhs.vpt); + count = 0; + + /* + * @todo - tmpl_dcursor, which handles more things. But + * that isn't done yet. So we hack stuff here. + */ + last = NULL; + for (vp = fr_pair_list_head(list); vp; vp = next) { + next = fr_pair_list_next(list, vp); + if (da == vp->da) { + if ((num >= 0) && (count == num)) { + if (fr_edit_list_pair_delete(state->el, list, vp) < 0) return -1; + break; + } + + if (num == NUM_ALL) { + if (fr_edit_list_pair_delete(state->el, list, vp) < 0) return -1; + continue; + } + + if (num == NUM_LAST) { + last = vp; + continue; + } + + count++; + } + } + + /* + * Delete the last one. + */ + if (last) return fr_edit_list_pair_delete(state->el, list, last); + + return 0; +} + /** Apply the edits. Broken out for simplicity * * The edits are applied as: @@ -244,6 +301,11 @@ static int apply_edits(request_t *request, unlang_frame_state_edit_t *state, map fr_assert(rhs_box->type == FR_TYPE_STRING); + if (map->op == T_OP_SUB_EQ) { + REDEBUG("Cannot remove data from a list"); + return -1; + } + da = state->lhs.vp->da; if (fr_type_is_group(da->type)) da = fr_dict_root(request->dict); @@ -265,10 +327,27 @@ static int apply_edits(request_t *request, unlang_frame_state_edit_t *state, map * If it's not data, it must be an attribute or a list. */ if (!tmpl_is_attr(state->rhs.vpt) && !tmpl_is_list(state->rhs.vpt)) { - RERROR("Unknown RHS %s", state->rhs.vpt->name); + REDEBUG("Unknown RHS %s", state->rhs.vpt->name); return -1; } + /* + * Remove an attribute from a list. + * + * @todo - ensure RHS is only an attribute which is + * parented from the LHS, and that it has no list + * reference? This probably needs to be done in + * unlang_fixup_edit() + */ + if (map->op == T_OP_SUB_EQ) { + if (!tmpl_is_attr(state->rhs.vpt)) { + REDEBUG("Cannot remove ??? from list"); + return -1; + } + + return remove_vps(request, state, map); + } + /* * Find the RHS attribute / list. * @@ -278,7 +357,7 @@ static int apply_edits(request_t *request, unlang_frame_state_edit_t *state, map * FOO"? */ if (tmpl_find_vp(&vp, request, state->rhs.vpt) < 0) { - RERROR("Can't find %s", state->rhs.vpt->name); + REDEBUG("Can't find %s", state->rhs.vpt->name); return -1; } @@ -329,7 +408,7 @@ static int apply_edits(request_t *request, unlang_frame_state_edit_t *state, map * etc. */ if (!fr_dict_attr_compatible(state->lhs.vp->da, vp->da)) { - RERROR("DAs are incompatible (%s vs %s)", + REDEBUG("DAs are incompatible (%s vs %s)", state->lhs.vp->da->name, vp->da->name); return -1; } @@ -378,7 +457,7 @@ leaf: */ if (!tmpl_is_attr(state->lhs.vpt) || !state->lhs.vp || !fr_type_is_leaf(state->lhs.vp->vp_type)) { - RERROR("Cannot assign data to list %s", map->lhs->name); + REDEBUG("Cannot assign data to list %s", map->lhs->name); return -1; } diff --git a/src/tests/keywords/edit-list-remove b/src/tests/keywords/edit-list-remove new file mode 100644 index 0000000000..6378ef16f9 --- /dev/null +++ b/src/tests/keywords/edit-list-remove @@ -0,0 +1,70 @@ +# +# PRE: edit-list +# + +update request { + &Tmp-Octets-0 := 0x00 + &Tmp-String-0 := "foo" + &Tmp-Integer-0 := 1 +} + +# Does exist +if (!&request.Tmp-String-0) { + test_fail +} + +# Remove the first one +&request -= &Tmp-String-0 + +# Does not exist +if (&request.Tmp-String-0) { + test_fail +} + +# Other things still exist +if (!&request.Tmp-Octets-0) { + test_fail +} + +if (!&request.Tmp-Integer-0) { + test_fail +} + +# +# Add multiple of the same type +# +update request { + &Tmp-String-0 := "foo" + &Tmp-String-0 += "bar" + &Tmp-String-0 += "baz" +} + +if (!&request.Tmp-String-0) { + test_fail +} + +&request -= &Tmp-String-0[0] + +# the first one has been removed +if (&request.Tmp-String-0[0] != "bar") { + test_fail +} + +# Other things still exist +if (!&request.Tmp-Octets-0) { + test_fail +} + +if (!&request.Tmp-Integer-0) { + test_fail +} + +&request -= &Tmp-String-0[*] + +# Does not exist +if (&request.Tmp-String-0) { + test_fail +} + + +success -- 2.47.2