*/
#include <freeradius-devel/util/pair.h>
#include <freeradius-devel/util/talloc.h>
+#include <freeradius-devel/util/proto.h>
+#include <freeradius-devel/util/pair_legacy.h>
/** Print the value of an attribute to a string
*
* For structural types descend down
*/
case FR_TYPE_STRUCTURAL:
- /*
- * Serialize all child VPs as full quoted
- * <pair> = ["]<child>["]
- */
our_out = FR_SBUFF(out);
FR_SBUFF_IN_CHAR_RETURN(&our_out, '{', ' ');
FR_SBUFF_SET_RETURN(out, &our_out);
}
+static ssize_t fr_pair_list_print_unflatten(fr_sbuff_t *out, fr_da_stack_t const *da_stack, unsigned int depth, fr_pair_list_t const *list, fr_pair_t **vp_p)
+{
+ fr_dict_attr_t const *parent;
+ fr_pair_t *vp = *vp_p;
+ fr_pair_t *next = fr_pair_list_next(list, vp);
+ fr_sbuff_t our_out = FR_SBUFF(out);
+
+ fr_assert(depth >= 1);
+
+ /*
+ * Not yet at the correct depth, print more parents.
+ */
+ if (depth < vp->da->depth) {
+ FR_SBUFF_IN_STRCPY_RETURN(&our_out, da_stack->da[depth - 1]->name);
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, " = { ");
+
+ FR_SBUFF_RETURN(fr_pair_list_print_unflatten, &our_out, da_stack, depth + 1, list, vp_p);
+ FR_SBUFF_IN_STRCPY_RETURN(&our_out, " }");
+ FR_SBUFF_SET_RETURN(out, &our_out);
+ }
+
+ parent = vp->da->parent;
+
+ /*
+ * We are at the correct depth. Print out all of the VPs which match the parent at this depth.
+ */
+ while (vp->da->parent == parent) {
+ next = fr_pair_list_next(list, vp);
+
+ FR_SBUFF_RETURN(fr_pair_print, &our_out, vp->da->parent, vp);
+
+ /*
+ * Flat structures are listed in order. Which means as soon as we go from 1..N back to
+ * 1, we have ended the current structure.
+ */
+ if (next && (vp->da->parent->type == FR_TYPE_STRUCT) &&
+ (next->da->parent == vp->da->parent) &&
+ (next->da->attr < vp->da->attr)) {
+ break;
+ }
+
+ vp = next;
+ if (!vp) break;
+
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, ", ");
+ }
+
+ *vp_p = next;
+ FR_SBUFF_SET_RETURN(out, &our_out);
+}
+
/** Print a pair list
*
* @param[in] out Where to write the string.
- * @param[in] parent parent da to start from.
+ * @param[in] parent parent da to start from
* @param[in] list pair list
* @return
* - Length of data written to out.
*/
ssize_t fr_pair_list_print(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_pair_list_t const *list)
{
- fr_pair_t *vp;
- fr_sbuff_t our_out = FR_SBUFF(out);
+ fr_pair_t *vp;
+ fr_sbuff_t our_out = FR_SBUFF(out);
vp = fr_pair_list_head(list);
if (!vp) {
return fr_sbuff_used(out);
}
+ /*
+ * Groups are printed from the root.
+ */
+ if (parent && (parent->type == FR_TYPE_GROUP)) parent = NULL;
+
while (true) {
- FR_SBUFF_RETURN(fr_pair_print, &our_out, parent, vp);
+ unsigned int depth;
+ fr_da_stack_t da_stack;
+
+ fr_proto_da_stack_build(&da_stack, vp->da);
+
+ if (!fr_pair_legacy_print_nested ||
+ (!parent && (vp->da->depth == 1)) ||
+ (parent && (fr_dict_by_da(parent) != fr_dict_by_da(vp->da))) ||
+ (parent && (da_stack.da[parent->depth] == parent) && (parent->depth + 1 == vp->da->depth))) {
+ FR_SBUFF_RETURN(fr_pair_print, &our_out, parent, vp);
+ vp = fr_pair_list_next(list, vp);
+
+ } else {
+ /*
+ * We have to print a partial tree, starting from the root.
+ */
+ if (!parent) {
+ depth = 1;
+ } else {
+ depth = parent->depth + 1;
+ }
+
+ FR_SBUFF_RETURN(fr_pair_list_print_unflatten, &our_out, &da_stack, depth, list, &vp);
+ }
- vp = fr_pair_list_next(list, vp);
if (!vp) break;
FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, ", ");
--- /dev/null
+#
+# Tests for parsing files ala radclient or "users"
+#
+# $Id$
+#
+
+proto-dictionary radius
+
+#
+# The same as "file.txt", but where we set a migration flag which prints flat attributes as nested.
+#
+migrate pair_legacy_print_nested = true
+
+#
+# Fully specified paths.
+#
+# @todo - we arguably want to force nesting on these attributes? Or change the nesting when printed?
+#
+read_file files/cisco_avpair.txt
+match User-Name = "bob", User-Password = "hello", Vendor-Specific = { Cisco = { AVPair = "1", AVPair += "2", AVPair += "3", AVPair += "4" } }
+
+#
+# Relative attributes, all on the same line.
+#
+read_file files/cisco_relative.txt
+match User-Name = "bob", User-Password = "hello", Vendor-Specific = { Cisco = { AVPair = "1", AVPair += "2", AVPair += "3", AVPair += "4" } }
+
+#
+# Relative attributes, each on a different line
+#
+read_file files/cisco_multiline_relative.txt
+match User-Name = "bob", User-Password = "hello", Vendor-Specific = { Cisco = { AVPair = "1", AVPair += "2", AVPair += "3", AVPair += "4" } }
+
+
+#
+# Multiple Cisco AVPAir, all on one line
+#
+read_file files/cisco_single_line.txt
+match User-Name = "bob", User-Password = "hello", Vendor-Specific = { Cisco = { AVPair = "1", AVPair += "2", AVPair += "3", AVPair += "4" } }
+
+count
+match 10