From: Alan T. DeKok Date: Wed, 9 Feb 2022 20:03:52 +0000 (-0500) Subject: add xlat_tokenize_ephemeral_expression() and tests X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e3cba58f455165b3d14037ab97161a181972e353;p=thirdparty%2Ffreeradius-server.git add xlat_tokenize_ephemeral_expression() and tests so that the new xlat expressions can be tested with something more than just parsing --- diff --git a/src/bin/unit_test_module.c b/src/bin/unit_test_module.c index ce78013a5f8..378e594dada 100644 --- a/src/bin/unit_test_module.c +++ b/src/bin/unit_test_module.c @@ -412,6 +412,50 @@ static bool do_xlats(fr_event_list_t *el, char const *filename, FILE *fp) continue; } + /* + * Look for "xlat_expr" + */ + if (strncmp(input, "xlat_expr ", 10) == 0) { + ssize_t slen; + TALLOC_CTX *xlat_ctx = talloc_init_const("xlat"); + char *fmt = talloc_typed_strdup(xlat_ctx, input + 10); + xlat_exp_t *head = NULL; + + slen = xlat_tokenize_ephemeral_expression(xlat_ctx, &head, el, NULL, + &FR_SBUFF_IN(fmt, talloc_array_length(fmt) - 1), + NULL, + &(tmpl_rules_t) { + .attr = { + .dict_def = dict_protocol, + .allow_unresolved = true, + } + } + ); + if (slen <= 0) { + talloc_free(xlat_ctx); + snprintf(output, sizeof(output), "ERROR offset %d '%s'", (int) -slen, + fr_strerror()); + continue; + } + + if (input[slen + 10] != '\0') { + talloc_free(xlat_ctx); + snprintf(output, sizeof(output), "ERROR offset %d Unexpected text '%s' after parsing", + (int) slen, input + slen + 10); + continue; + } + + len = xlat_eval_compiled(output, sizeof(output), request, head, NULL, NULL); + if (len < 0) { + talloc_free(xlat_ctx); + snprintf(output, sizeof(output), "ERROR expanding xlat: %s", fr_strerror()); + continue; + } + + TALLOC_FREE(xlat_ctx); /* also frees 'head' */ + continue; + } + /* * Look for "data". */ diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index a8217bd5593..0fa7cd02635 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -295,6 +295,11 @@ bool xlat_async_required(xlat_exp_t const *xlat); ssize_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules); +ssize_t xlat_tokenize_ephemeral_expression(TALLOC_CTX *ctx, xlat_exp_t **head, + fr_event_list_t *el, + xlat_flags_t *flags, fr_sbuff_t *in, + fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules); + ssize_t xlat_tokenize_ephemeral(TALLOC_CTX *ctx, xlat_exp_t **head, fr_event_list_t *el, xlat_flags_t *flags, fr_sbuff_t *in, diff --git a/src/lib/unlang/xlat_expr.c b/src/lib/unlang/xlat_expr.c index 87c9a7e9a36..bae29a012c1 100644 --- a/src/lib/unlang/xlat_expr.c +++ b/src/lib/unlang/xlat_expr.c @@ -1456,8 +1456,8 @@ ssize_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_ ssize_t slen; fr_sbuff_parse_rules_t *bracket_rules = NULL; fr_sbuff_parse_rules_t *terminal_rules = NULL; - xlat_flags_t my_flags = { 0 }; - tmpl_rules_t my_rules = { 0 }; + xlat_flags_t my_flags = { }; + tmpl_rules_t my_rules = { }; /* * Whatever the caller passes, ensure that we have a @@ -1495,11 +1495,21 @@ ssize_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_ talloc_free(bracket_rules); talloc_free(terminal_rules); - if (slen <= 0) return slen; + if (slen < 0) return slen; + + /* + * Zero length expansion, return a zero length node. + */ + if (!*head) { + *head = xlat_exp_alloc(ctx, XLAT_BOX, "", 0); + return 0; + } /* * Add nodes that need to be bootstrapped to * the registry. + * + * @todo - can't do this for ephemeral ones! */ if (xlat_bootstrap(*head) < 0) { TALLOC_FREE(*head); @@ -1508,3 +1518,93 @@ ssize_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_ return slen; } + +/** Tokenize an xlat expression at runtime + * + * This function is only for testing. It should be deleted when + * expressions are integrated into the main xlat parser. + * + * @param[in] ctx to allocate dynamic buffers in. + * @param[out] head the head of the xlat list / tree structure. + * @param[in] el for registering any I/O handlers. + * @param[out] flags indicating the state of the ephemeral tree. + * @param[in] in the format string to expand. + * @param[in] p_rules from the encompassing grammar. + * @param[in] t_rules controlling how attribute references are parsed. + * @return + * - >0 on success. + * - 0 and *head == NULL - Parse failure on first char. + * - 0 and *head != NULL - Zero length expansion + * - <0 the negative offset of the parse failure. + */ +ssize_t xlat_tokenize_ephemeral_expression(TALLOC_CTX *ctx, xlat_exp_t **head, + fr_event_list_t *el, + xlat_flags_t *flags, fr_sbuff_t *in, + fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules) +{ + ssize_t slen; + fr_sbuff_parse_rules_t *bracket_rules = NULL; + fr_sbuff_parse_rules_t *terminal_rules = NULL; + xlat_flags_t my_flags = { }; + tmpl_rules_t my_rules = { }; + + /* + * Whatever the caller passes, ensure that we have a + * terminal rule which ends on operators, and a terminal + * rule which ends on ')'. + */ + MEM(bracket_rules = talloc_zero(ctx, fr_sbuff_parse_rules_t)); + MEM(terminal_rules = talloc_zero(ctx, fr_sbuff_parse_rules_t)); + if (p_rules) { + *bracket_rules = *p_rules; + *terminal_rules = *p_rules; + + if (p_rules->terminals) { + MEM(terminal_rules->terminals = fr_sbuff_terminals_amerge(bracket_rules, + p_rules->terminals, + &operator_terms)); + } else { + terminal_rules->terminals = &operator_terms; + } + } else { + terminal_rules->terminals = &operator_terms; + } + MEM(bracket_rules->terminals = fr_sbuff_terminals_amerge(bracket_rules, + terminal_rules->terminals, + &bracket_terms)); + + if (!flags) flags = &my_flags; + + if (t_rules) { + my_rules = *t_rules; + } + my_rules.xlat.runtime_el = el; + + *head = NULL; + + slen = tokenize_expression(ctx, head, flags, in, terminal_rules, t_rules, T_INVALID, FR_TYPE_NULL, + bracket_rules, NULL); + talloc_free(bracket_rules); + talloc_free(terminal_rules); + + if (slen < 0) return slen; + + /* + * Zero length expansion, return a zero length node. + */ + if (!*head) { + *head = xlat_exp_alloc(ctx, XLAT_BOX, "", 0); + return 0; + } + + /* + * Create ephemeral instance data for the xlat + */ + if (xlat_instantiate_ephemeral(*head, el) < 0) { + fr_strerror_const("Failed performing ephemeral instantiation for xlat"); + TALLOC_FREE(*head); + return 0; + } + + return slen; +}