From: Alan T. DeKok Date: Tue, 8 Aug 2023 19:59:20 +0000 (-0400) Subject: add and use fr_pair_legacy_print_nested flag X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed6c4480a04b6130156fba34308bc8b2e633de89;p=thirdparty%2Ffreeradius-server.git add and use fr_pair_legacy_print_nested flag so that we can update the tests to print flat attributes as nested, without updating all of the tests. The next step is to set the flag in the tests, so that the tests will parse flat attributes, but will always print nested ones. --- diff --git a/src/bin/unit_test_attribute.c b/src/bin/unit_test_attribute.c index edadb9d1ecc..662a295a649 100644 --- a/src/bin/unit_test_attribute.c +++ b/src/bin/unit_test_attribute.c @@ -2385,6 +2385,10 @@ static size_t command_migrate(command_result_t *result, UNUSED command_file_ctx_ p += sizeof("pair_legacy_nested") - 1; out = &fr_pair_legacy_nested; + } else if (strncmp(p, "pair_legacy_print_nested", sizeof("pair_legacy_print_nested") - 1) == 0) { + p += sizeof("pair_legacy_print_nested") - 1; + out = &fr_pair_legacy_print_nested; + } else if (strncmp(p, "parse_new_conditions", sizeof("parse_new_conditions") - 1) == 0) { p += sizeof("parse_new_conditions") - 1; out = &parse_new_conditions; diff --git a/src/lib/util/pair_legacy.c b/src/lib/util/pair_legacy.c index f309ca0a783..db63beeed5d 100644 --- a/src/lib/util/pair_legacy.c +++ b/src/lib/util/pair_legacy.c @@ -33,6 +33,7 @@ RCSID("$Id$") #include bool fr_pair_legacy_nested = false; +bool fr_pair_legacy_print_nested = false; static fr_sbuff_term_t const bareword_terminals = FR_SBUFF_TERMS( diff --git a/src/lib/util/pair_legacy.h b/src/lib/util/pair_legacy.h index 065f54e0aa8..f0354377e91 100644 --- a/src/lib/util/pair_legacy.h +++ b/src/lib/util/pair_legacy.h @@ -34,7 +34,8 @@ RCSIDH(pair_legacy_h, "$Id$") extern "C" { #endif -extern bool fr_pair_legacy_nested; /* for migratipon from legacy flat to new nested */ +extern bool fr_pair_legacy_nested; /* parsing: for migration from legacy flat to new nested */ +extern bool fr_pair_legacy_print_nested; /* printing: for migration from legacy flat to new nested */ fr_token_t fr_pair_list_afrom_str(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, char const *buffer, size_t len, fr_pair_list_t *head); diff --git a/src/lib/util/pair_print.c b/src/lib/util/pair_print.c index 5241d2d35a9..22f049818f4 100644 --- a/src/lib/util/pair_print.c +++ b/src/lib/util/pair_print.c @@ -22,6 +22,8 @@ */ #include #include +#include +#include /** Print the value of an attribute to a string * @@ -46,10 +48,6 @@ ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_toke * For structural types descend down */ case FR_TYPE_STRUCTURAL: - /* - * Serialize all child VPs as full quoted - * = ["]["] - */ our_out = FR_SBUFF(out); FR_SBUFF_IN_CHAR_RETURN(&our_out, '{', ' '); @@ -198,10 +196,61 @@ ssize_t fr_pair_print_secure(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_p 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. @@ -209,8 +258,8 @@ ssize_t fr_pair_print_secure(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_p */ 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) { @@ -218,10 +267,37 @@ ssize_t fr_pair_list_print(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_pai 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, ", "); diff --git a/src/tests/unit/file_unflatten.txt b/src/tests/unit/file_unflatten.txt new file mode 100644 index 00000000000..84698140432 --- /dev/null +++ b/src/tests/unit/file_unflatten.txt @@ -0,0 +1,42 @@ +# +# 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