*/
#define pair_delete(_list, _pair_or_da) \
_Generic((_pair_or_da), \
- fr_dict_attr_t const * : fr_pair_delete_by_da(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)), \
- fr_dict_attr_t * : fr_pair_delete_by_da(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)), \
+ fr_dict_attr_t const * : fr_pair_delete_by_da_nested(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)), \
+ fr_dict_attr_t * : fr_pair_delete_by_da_nested(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)), \
fr_pair_t * : fr_pair_delete(_list, UNCONST(fr_pair_t *, _pair_or_da)) \
)
return cnt;
}
+/** Delete matching pairs from the specified list
+ *
+ * @param[in,out] list to search for attributes in or delete attributes from.
+ * @param[in] da to match.
+ * @return
+ * - >0 the number of pairs deleted.
+ * - 0 if no pairs were deleted.
+ */
+int fr_pair_delete_by_da_nested(fr_pair_list_t *list, fr_dict_attr_t const *da)
+{
+ int cnt = 0;
+ fr_pair_t *vp;
+ fr_dict_attr_t const **find; /* DA currently being looked for */
+ fr_pair_list_t *cur_list; /* Current list being searched */
+ fr_da_stack_t da_stack;
+
+ if (da->depth <= 1) return fr_pair_delete_by_da(list, da);
+
+ if (fr_pair_list_empty(list)) return 0;
+
+ /*
+ * Similar to fr_pair_find_by_da_nested()
+ */
+ fr_proto_da_stack_build(&da_stack, da);
+ cur_list = list;
+ find = &da_stack.da[0];
+ vp = NULL;
+
+ /*
+ * Loop over the list at each level until we find a matching da.
+ */
+ while (true) {
+ fr_pair_t *next;
+
+ fr_assert((*find)->depth <= da->depth);
+
+ /*
+ * Find a vp which matches a given da. If found,
+ * recurse into the child list to find the child
+ * attribute.
+ *
+ */
+ next = fr_pair_find_by_da(cur_list, vp, *find);
+ if (next) {
+ /*
+ * We've found a match for the requested
+ * da - delete it
+ */
+ if ((*find) == da) {
+ do {
+ fr_pair_delete(cur_list, next);
+ cnt++;
+ } while ((next = fr_pair_find_by_da(cur_list, vp, *find)) != NULL);
+
+ return cnt;
+ }
+
+ /*
+ * Prepare to search the next level.
+ */
+ cur_list = &next->vp_group;
+ find++;
+ vp = NULL;
+ continue;
+ }
+
+ /*
+ * We hit the end of the top-level list. Therefore we found nothing.
+ */
+ if (cur_list == list) break;
+
+ /*
+ * We hit the end of *A* list. Go to the parent
+ * VP, and then find its list.
+ *
+ * We still then have to go to the next attribute
+ * in the parent list, as we've checked all of the
+ * children of this VP.
+ */
+ find--;
+ vp = fr_pair_list_parent(cur_list);
+ cur_list = fr_pair_parent_list(vp);
+ }
+
+ return fr_pair_delete_by_da(list, da);
+}
+
/** Delete matching pairs from the specified list
*
* @param[in] list to delete attributes from.
int fr_pair_delete_by_da(fr_pair_list_t *head, fr_dict_attr_t const *da) CC_HINT(nonnull);
+int fr_pair_delete_by_da_nested(fr_pair_list_t *list, fr_dict_attr_t const *da) CC_HINT(nonnull);
+
fr_pair_t *fr_pair_delete(fr_pair_list_t *list, fr_pair_t *vp) CC_HINT(nonnull);
/* functions for FR_TYPE_STRUCTURAL */
talloc_free(parent); /* not vp! */
}
+static void test_fr_pair_delete_by_da_nested(void)
+{
+ int count;
+ fr_pair_t *vp, *parent = NULL;
+ fr_pair_list_t local_pairs;
+
+ fr_pair_list_init(&local_pairs);
+
+ TEST_CASE("Allocation using fr_pair_afrom_da_nested");
+ TEST_CHECK((vp = fr_pair_afrom_da_nested(autofree, &local_pairs, fr_dict_attr_test_tlv_string)) != NULL);
+
+ TEST_CHECK(vp && vp->da == fr_dict_attr_test_tlv_string);
+ TEST_MSG("Expected attr(%s) == vp->da->attr(%s)", fr_dict_attr_test_tlv_string->name, vp->da->name);
+
+ TEST_CASE("Deleted nested pair");
+ TEST_CHECK((count = fr_pair_delete_by_da_nested(&local_pairs, fr_dict_attr_test_tlv_string)) == 1);
+
+ TEST_CASE("Top list still has the tlv attribute");
+ parent = fr_pair_find_by_da(&local_pairs, NULL, fr_dict_attr_test_tlv);
+ TEST_ASSERT(parent != NULL);
+
+ TEST_CASE("Parent list does not have the tlv child attribute");
+ TEST_CHECK(fr_pair_find_by_da(&parent->vp_group, NULL, fr_dict_attr_test_tlv_string) == NULL);
+
+ fr_pair_list_free(&local_pairs);
+}
+
static void test_fr_pair_copy(void)
{
fr_pair_t *vp, *copy;
{ "fr_pair_update_by_da_parent", test_fr_pair_update_by_da_parent },
{ "fr_pair_delete", test_fr_pair_delete },
{ "fr_pair_delete_by_da", test_fr_pair_delete_by_da },
+ { "fr_pair_delete_by_da_nested", test_fr_pair_delete_by_da_nested },
/* Compare */
{ "fr_pair_cmp", test_fr_pair_cmp },