]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
move xlats to dlists.
authorAlan T. DeKok <aland@freeradius.org>
Fri, 13 May 2022 18:31:08 +0000 (14:31 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 16 May 2022 19:00:24 +0000 (15:00 -0400)
src/lib/unlang/xlat.h
src/lib/unlang/xlat_builtin.c
src/lib/unlang/xlat_expr.c
src/lib/unlang/xlat_priv.h
src/lib/unlang/xlat_tokenize.c

index 45e198de42c976453b3ab7717f601591fb806410..85509250dc8253c53a595dfdf8a97820537e7fca 100644 (file)
@@ -379,8 +379,6 @@ void                xlat_free(void);
  */
 xlat_exp_t     *xlat_exp_func_alloc(TALLOC_CTX *ctx, xlat_t *func, xlat_exp_head_t const *args);
 
-void           xlat_exp_free(xlat_exp_head_t **head);
-
 tmpl_t         *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t *xlat);
 
 int            xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t **head, tmpl_t **vpt_p);
index b0e2dade7adf58361f64b80a5d79fef15bfc3766..281b0ae9c04b8c9f88c1e93c20ba598c515aaafe 100644 (file)
@@ -752,7 +752,8 @@ static int xlat_redundant_instantiate(xlat_inst_ctx_t const *xctx)
                 *      correctly.
                 */
                MEM(head = xlat_exp_head_alloc(xri->ex));
-               MEM(head->next = node = xlat_exp_func_alloc(head, xrf->func, xctx->ex->call.args));
+               MEM(node = xlat_exp_func_alloc(head, xrf->func, xctx->ex->call.args));
+               xlat_exp_insert_tail(head, node);
 
                switch (xrf->func->input_type) {
                case XLAT_INPUT_UNPROCESSED:
@@ -797,7 +798,7 @@ static int xlat_redundant_instantiate(xlat_inst_ctx_t const *xctx)
         *      a copy of the original arguments with each
         *      function that's called.
         */
-       xlat_exp_free(&xctx->ex->call.args);
+       fr_dlist_talloc_free(&xctx->ex->call.args->dlist);
 
        return 0;
 }
index d673c644309b6b4b4faccf279c427f95c006e70c..84c3858bedc62cbfc4fdb6889cd8c182def01b46 100644 (file)
@@ -97,6 +97,8 @@ static fr_slen_t xlat_expr_print_binary(fr_sbuff_t *out, xlat_exp_t const *node,
        size_t  at_in = fr_sbuff_used_total(out);
        xlat_exp_t *child = xlat_exp_head(node->call.args);
 
+       fr_assert(child != NULL);
+
        FR_SBUFF_IN_CHAR_RETURN(out, '(');
        xlat_print_node(out, node->call.args, child, e_rules); /* prints a space after the first argument */
 
@@ -106,7 +108,10 @@ static fr_slen_t xlat_expr_print_binary(fr_sbuff_t *out, xlat_exp_t const *node,
         */
        FR_SBUFF_IN_STRCPY_RETURN(out, fr_tokens[node->call.func->token]);
        FR_SBUFF_IN_CHAR_RETURN(out, ' ');
+
        child = xlat_exp_next(node->call.args, child);
+       fr_assert(child != NULL);
+
        xlat_print_node(out, node->call.args, child, e_rules);
 
        FR_SBUFF_IN_CHAR_RETURN(out, ')');
@@ -116,7 +121,7 @@ static fr_slen_t xlat_expr_print_binary(fr_sbuff_t *out, xlat_exp_t const *node,
 
 static void xlat_func_append_arg(xlat_exp_t *head, xlat_exp_t *node)
 {
-       xlat_exp_t *group, **tail;
+       xlat_exp_t *group;
 
        fr_assert(head->type == XLAT_FUNC);
        fr_assert(node->type != XLAT_GROUP);
@@ -128,16 +133,12 @@ static void xlat_func_append_arg(xlat_exp_t *head, xlat_exp_t *node)
        group->fmt = node->fmt; /* not entirely correct, but good enough for now */
 
        MEM(group->group = xlat_exp_head_alloc(group));
-       group->group->next = talloc_steal(group->group, node);
+       talloc_steal(group->group, node);
+       xlat_exp_insert_tail(group->group, node);
 
-       group->flags = node->flags;
-       xlat_flags_merge(&head->call.args->flags, &group->flags);
-       xlat_flags_merge(&head->flags, &group->flags);
+       xlat_exp_insert_tail(head->call.args, group);
 
-       tail = &head->call.args->next;
-       while (*tail) tail = &((*tail)->next);
-
-       *tail = group;
+       xlat_flags_merge(&head->flags, &head->call.args->flags);
 }
 
 
@@ -764,8 +765,9 @@ static ssize_t tokenize_unary(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_
                              fr_sbuff_parse_rules_t const *bracket_rules)
 {
        ssize_t                 slen;
-       xlat_exp_t              *unary = NULL;
+       xlat_exp_t              *node, *unary = NULL;
        xlat_t                  *func = NULL;
+       char const              *fmt = NULL;
        fr_sbuff_t              our_in = FR_SBUFF(in);
 
        XLAT_DEBUG("UNARY <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
@@ -776,10 +778,12 @@ static ssize_t tokenize_unary(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_
         *      we allocate.
         */
        if (fr_sbuff_next_if_char(&our_in, '!')) { /* unary not */
+               fmt = "!";
                func = xlat_func_find("unary_not", 9);
                fr_assert(func != NULL);
        }
        else if (fr_sbuff_next_if_char(&our_in, '-')) { /* unary minus */
+               fmt = "-";
                func = xlat_func_find("unary_minus", 11);
                fr_assert(func != NULL);
        }
@@ -797,23 +801,29 @@ static ssize_t tokenize_unary(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_
        
        MEM(unary = xlat_exp_alloc(head, XLAT_FUNC, func->name, strlen(func->name)));
        MEM(unary->call.args = xlat_exp_head_alloc(unary));
+       unary->fmt = fmt;
        unary->call.func = func;
        unary->flags = func->flags;
 
-       slen = tokenize_field(unary->call.args, &unary->call.args->next, &our_in, p_rules, t_rules, bracket_rules);
+       slen = tokenize_field(unary->call.args, &node, &our_in, p_rules, t_rules, bracket_rules);
        if (slen <= 0) {
                talloc_free(unary);
                FR_SBUFF_ERROR_RETURN_ADJ(&our_in, slen);
        }
 
-       if (!xlat_exp_head(unary->call.args)) {
+       if (!node) {
                fr_strerror_const("Empty expression is invalid");
                FR_SBUFF_ERROR_RETURN_ADJ(&our_in, -slen);
        }
 
-       *out = unary;
-       xlat_flags_merge(&head->flags, &unary->flags);
+       xlat_exp_insert_tail(unary->call.args, node);
+       xlat_flags_merge(&unary->flags, &unary->call.args->flags);
 
+       /*
+        *      Don't add it to head->flags, that will be done when it's actually inserted.
+        */
+
+       *out = unary;
        return fr_sbuff_set(in, &our_in);
 }
 
@@ -979,7 +989,6 @@ done:
 #endif
 
        *out = node;
-       xlat_flags_merge(&head->flags, &node->flags);
 
        return fr_sbuff_set(in, &our_in);
 }
@@ -1023,6 +1032,9 @@ static size_t const expr_assignment_op_table_len = NUM_ELEMENTS(expr_assignment_
  *     (EXPR)
  *     !EXPR
  *     A OP B
+ *
+ *     If "out" is NULL then the expression is added to "head".
+ *     Otherwise, it's returned to the caller.
  */
 static ssize_t tokenize_expression(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in,
                                   fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
@@ -1060,7 +1072,13 @@ redo:
         */
        if (fr_sbuff_extend(&our_in) == 0) {
        done:
-               *out = lhs;
+
+               if (!out) {
+                       xlat_exp_insert_tail(head, lhs);
+               } else {
+                       *out = lhs;
+               }
+
                return fr_sbuff_set(in, &our_in);
        }
 
@@ -1181,8 +1199,6 @@ redo:
        }
 
        /*
-        *      @todo - purify the node.
-        *
         *      @todo - also if the have differenting data types on the LHS and RHS, and one of them is an
         *      XLAT_BOX, then try to upcast the XLAT_BOX to the destination data type before returning.  This
         *      optimization minimizes the amount of run-time work we have to do.
@@ -1200,6 +1216,8 @@ redo:
        xlat_func_append_arg(node, lhs);
        xlat_func_append_arg(node, rhs);
 
+       fr_assert(xlat_exp_head(node->call.args) != NULL);
+
        lhs = node;
        goto redo;
 }
@@ -1268,7 +1286,7 @@ ssize_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuf
 
        MEM(head = xlat_exp_head_alloc(ctx));
 
-       slen = tokenize_expression(head, &head->next, in, terminal_rules, t_rules, T_INVALID, bracket_rules);
+       slen = tokenize_expression(head, NULL, in, terminal_rules, t_rules, T_INVALID, bracket_rules);
        talloc_free(bracket_rules);
        talloc_free(terminal_rules);
 
@@ -1349,7 +1367,7 @@ ssize_t xlat_tokenize_ephemeral_expression(TALLOC_CTX *ctx, xlat_exp_head_t **ou
 
        MEM(head = xlat_exp_head_alloc(ctx));
 
-       slen = tokenize_expression(head, &head->next, in, terminal_rules, &my_rules, T_INVALID, bracket_rules);
+       slen = tokenize_expression(head, NULL, in, terminal_rules, &my_rules, T_INVALID, bracket_rules);
        talloc_free(bracket_rules);
        talloc_free(terminal_rules);
 
index 5b8baa687ee7cf68784b023d98fb251b26edf468..7a8ba508c618233d7cfc930fa63e819282700264 100644 (file)
@@ -120,7 +120,7 @@ struct xlat_exp {
        xlat_flags_t    flags;          //!< Flags that control resolution and evaluation.
 
        xlat_type_t     type;           //!< type of this expansion.
-       xlat_exp_t      *next;          //!< Next in the list.
+       fr_dlist_t      entry;
 
        union {
                xlat_exp_head_t *alternate[2];  //!< alternate expansions
@@ -151,7 +151,7 @@ struct xlat_exp {
 struct xlat_exp_head {
        char const      *fmt;           //!< The original format string (a talloced buffer).
        xlat_flags_t    flags;          //!< Flags that control resolution and evaluation.
-       xlat_exp_t      *next;          //!< Next in the list.
+       fr_dlist_head_t dlist;
 };
 
 
@@ -321,10 +321,10 @@ int               xlat_register_expressions(void);
 /*
  *     xlat_tokenize.c
  */
-int            xlat_tokenize_expansion(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in,
+int            xlat_tokenize_expansion(xlat_exp_head_t *head, fr_sbuff_t *in,
                                        tmpl_attr_rules_t const *t_rules);
 
-int            xlat_tokenize_function_args(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in,
+int            xlat_tokenize_function_args(xlat_exp_head_t *head, fr_sbuff_t *in,
                                            tmpl_attr_rules_t const *rules);
 
 ssize_t                xlat_print_node(fr_sbuff_t *out, xlat_exp_head_t const *head, xlat_exp_t const *node, fr_sbuff_escape_rules_t const *e_rules);
@@ -333,9 +333,9 @@ ssize_t             xlat_print_node(fr_sbuff_t *out, xlat_exp_head_t const *head, xlat_exp_
 
 static inline xlat_exp_t *xlat_exp_head(xlat_exp_head_t const *head)
 {
-       if (!head || !head->next) return NULL;
+       if (!head) return NULL;
 
-       return UNCONST(xlat_exp_t *, (head->next));
+       return fr_dlist_head(&head->dlist);
 }
 
 /** Iterate over the contents of a list, only one level
@@ -344,19 +344,28 @@ static inline xlat_exp_t *xlat_exp_head(xlat_exp_head_t const *head)
  * @param[in] _iter            Name of iteration variable.
  *                             Will be declared in the scope of the loop.
  */
-#define xlat_exp_foreach(_list_head, _iter) \
-       for (xlat_exp_t *_iter = xlat_exp_head(_list_head); _iter; _iter = _iter->next)
+#define xlat_exp_foreach(_list_head, _iter) fr_dlist_foreach(&((_list_head)->dlist), xlat_exp_t, _iter)
 
-static inline xlat_exp_t *xlat_exp_next(UNUSED xlat_exp_head_t const *head, xlat_exp_t const *item)
+static inline xlat_exp_t *xlat_exp_next(xlat_exp_head_t const *head, xlat_exp_t const *node)
 {
-       if (!item->next) return NULL;
+       return fr_dlist_next(&head->dlist, node);
+}
 
-       return UNCONST(xlat_exp_t *, item->next);
+static inline int xlat_exp_insert_tail(xlat_exp_head_t *head, xlat_exp_t *node)
+{
+       xlat_flags_merge(&head->flags, &node->flags);
+       return fr_dlist_insert_tail(&head->dlist, node);
 }
 
 static inline xlat_exp_head_t *xlat_exp_head_alloc(TALLOC_CTX *ctx)
 {
-       return talloc_zero(ctx, xlat_exp_head_t);
+       xlat_exp_head_t *head;
+
+       head = talloc_zero(ctx, xlat_exp_head_t);
+       if (!head) return NULL;
+
+       fr_dlist_init(&head->dlist, xlat_exp_t, entry);
+       return head;
 }
 
 #ifdef __cplusplus
index f65064bc3381fac29dd3086438da915cb018b363..3bdb7f48e06560e2dbc3ba34c016879e0c6e7ee7 100644 (file)
@@ -150,26 +150,10 @@ static inline CC_HINT(always_inline) void xlat_exp_set_name_buffer(xlat_exp_t *n
 }
 #endif
 
-/** Free a linked list of xlat nodes
- *
- * @param[in,out] head to free.  Will be set to NULL
- */
-void xlat_exp_free(xlat_exp_head_t **head)
-{
-       xlat_exp_t *to_free = xlat_exp_head(*head), *next;
-
-       while (to_free) {
-               next = to_free->next;
-               talloc_free(to_free);
-               to_free = next;
-       };
-       *head = NULL;
-}
-
 static int xlat_tokenize_string(xlat_exp_head_t *head, fr_sbuff_t *in, bool brace,
                                fr_sbuff_parse_rules_t const *p_rules, tmpl_attr_rules_t const *t_rules);
 
-static inline int xlat_tokenize_alternation(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in,
+static inline int xlat_tokenize_alternation(xlat_exp_head_t *head, fr_sbuff_t *in,
                                            tmpl_attr_rules_t const *t_rules, bool func_args)
 {
        xlat_exp_t      *node;
@@ -182,13 +166,13 @@ static inline int xlat_tokenize_alternation(xlat_exp_head_t *head, xlat_exp_t **
        MEM(node->alternate[1] = xlat_exp_head_alloc(node));
 
        if (func_args) {
-               if (xlat_tokenize_function_args(node->alternate[0], &node->alternate[0]->next, in, t_rules) < 0) {
+               if (xlat_tokenize_function_args(node->alternate[0], in, t_rules) < 0) {
                error:
                        talloc_free(node);
                        return -1;
                }
        } else {
-               if (xlat_tokenize_expansion(node->alternate[0], &node->alternate[0]->next, in, t_rules) < 0) goto error;
+               if (xlat_tokenize_expansion(node->alternate[0], in, t_rules) < 0) goto error;
        }
 
        if (!fr_sbuff_adv_past_str_literal(in, ":-")) {
@@ -208,7 +192,7 @@ static inline int xlat_tokenize_alternation(xlat_exp_head_t *head, xlat_exp_t **
        if (xlat_tokenize_string(node->alternate[1], in,
                                  true, &xlat_expansion_rules, t_rules) < 0) goto error;
 
-       if (!node->alternate[1]->next) {
+       if (!xlat_exp_head(node->alternate[1])) {
                talloc_free(node);
                fr_strerror_const("Empty expansion is invalid");
                goto error;
@@ -221,8 +205,7 @@ static inline int xlat_tokenize_alternation(xlat_exp_head_t *head, xlat_exp_t **
        xlat_flags_merge(&node->flags, &node->alternate[1]->flags);
 
 done:
-       xlat_flags_merge(&head->flags, &node->flags);
-       *out = node;
+       xlat_exp_insert_tail(head, node);
 
        return 0;
 }
@@ -234,7 +217,7 @@ done:
  * @verbatim %{<num>} @endverbatim
  *
  */
-static inline int xlat_tokenize_regex(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in)
+static inline int xlat_tokenize_regex(xlat_exp_head_t *head, fr_sbuff_t *in)
 {
        uint8_t                 num;
        xlat_exp_t              *node;
@@ -276,8 +259,7 @@ static inline int xlat_tokenize_regex(xlat_exp_head_t *head, xlat_exp_t **out, f
        fr_sbuff_marker_release(&m_s);
        fr_sbuff_next(in);      /* Skip '}' */
 
-       xlat_flags_merge(&head->flags, &node->flags);
-       *out = node;
+       xlat_exp_insert_tail(head, node);
 
        return 0;
 }
@@ -304,7 +286,7 @@ int xlat_validate_function_mono(xlat_exp_t *node)
  *     - 0 if the string was parsed into a function.
  *     - <0 on parse error.
  */
-static inline int xlat_tokenize_function_mono(xlat_exp_head_t *head, xlat_exp_t **out,
+static inline int xlat_tokenize_function_mono(xlat_exp_head_t *head,
                                              fr_sbuff_t *in,
                                              tmpl_attr_rules_t const *rules)
 {
@@ -332,7 +314,6 @@ static inline int xlat_tokenize_function_mono(xlat_exp_head_t *head, xlat_exp_t
        if (!fr_sbuff_is_char(in, ':')) {
                fr_strerror_const("Can't find function/argument separator");
        bad_function:
-               *out = NULL;
                fr_sbuff_set(in, &m_s);         /* backtrack */
                fr_sbuff_marker_release(&m_s);
                return -1;
@@ -398,8 +379,7 @@ static inline int xlat_tokenize_function_mono(xlat_exp_head_t *head, xlat_exp_t
        }
 
        xlat_flags_merge(&node->flags, &node->call.args->flags);
-       xlat_flags_merge(&head->flags, &node->flags);
-       *out = node;
+       xlat_exp_insert_tail(head, node);
 
        return 0;
 }
@@ -441,8 +421,7 @@ int xlat_validate_function_args(xlat_exp_t *node)
  *     - 0 if the string was parsed into a function.
  *     - <0 on parse error.
  */
-int xlat_tokenize_function_args(xlat_exp_head_t *head, xlat_exp_t **out,
-                               fr_sbuff_t *in,
+int xlat_tokenize_function_args(xlat_exp_head_t *head, fr_sbuff_t *in,
                                tmpl_attr_rules_t const *rules)
 {
        xlat_exp_t              *node;
@@ -469,7 +448,6 @@ int xlat_tokenize_function_args(xlat_exp_head_t *head, xlat_exp_t **out,
        if (!fr_sbuff_is_char(in, ':')) {
                fr_strerror_const("Can't find function/argument separator");
        bad_function:
-               *out = NULL;
                fr_sbuff_set(in, &m_s);         /* backtrack */
                fr_sbuff_marker_release(&m_s);
                return -1;
@@ -533,8 +511,7 @@ int xlat_tokenize_function_args(xlat_exp_head_t *head, xlat_exp_t **out,
                goto error;
        }
 
-       xlat_flags_merge(&head->flags, &node->flags);
-       *out = node;
+       xlat_exp_insert_tail(head, node);
 
        return 0;
 }
@@ -564,7 +541,7 @@ static int xlat_resolve_virtual_attribute(xlat_exp_t *node, tmpl_t *vpt)
 /** Parse an attribute ref or a virtual attribute
  *
  */
-static inline int xlat_tokenize_attribute(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in,
+static inline int xlat_tokenize_attribute(xlat_exp_head_t *head, fr_sbuff_t *in,
                                          fr_sbuff_parse_rules_t const *p_rules, tmpl_attr_rules_t const *t_rules)
 {
        ssize_t                 slen;
@@ -610,7 +587,6 @@ static inline int xlat_tokenize_attribute(xlat_exp_head_t *head, xlat_exp_t **ou
                 */
                if (err == TMPL_ATTR_ERROR_MISSING_TERMINATOR) fr_sbuff_set(in, &m_s);
        error:
-               *out = NULL;
                fr_sbuff_marker_release(&m_s);
                talloc_free(node);
                return -1;
@@ -684,13 +660,13 @@ done:
                goto error;
        }
 
-       xlat_flags_merge(&head->flags, &node->flags);
-       *out = node;
+       xlat_exp_insert_tail(head, node);
+
        fr_sbuff_marker_release(&m_s);
        return 0;
 }
 
-int xlat_tokenize_expansion(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in,
+int xlat_tokenize_expansion(xlat_exp_head_t *head, fr_sbuff_t *in,
                            tmpl_attr_rules_t const *t_rules)
 {
        size_t                  len;
@@ -714,14 +690,14 @@ int xlat_tokenize_expansion(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t
         *      %{...}:-bar}
         */
        if (fr_sbuff_adv_past_str_literal(in, "%{")) {
-               return xlat_tokenize_alternation(head, out, in, t_rules, false);
+               return xlat_tokenize_alternation(head, in, t_rules, false);
        }
 
        /*
         *      %(...):-bar}
         */
        if (fr_sbuff_adv_past_str_literal(in, "%(")) {
-               return xlat_tokenize_alternation(head, out, in, t_rules, true);
+               return xlat_tokenize_alternation(head, in, t_rules, true);
        }
 
        /*
@@ -739,7 +715,7 @@ int xlat_tokenize_expansion(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t
        if (fr_sbuff_is_digit(in)) {
                int ret;
 
-               ret = xlat_tokenize_regex(head, out, in);
+               ret = xlat_tokenize_regex(head, in);
                if (ret <= 0) return ret;
 
                /* ret==1 means "nope, it's an attribute" */
@@ -801,7 +777,7 @@ int xlat_tokenize_expansion(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t
                fr_sbuff_set(in, &s_m);         /* backtrack */
                fr_sbuff_marker_release(&s_m);
 
-               return xlat_tokenize_function_mono(head, out, in, t_rules);
+               return xlat_tokenize_function_mono(head, in, t_rules);
 
        /*
         *      Hint token is a:
@@ -814,7 +790,7 @@ int xlat_tokenize_expansion(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t
                fr_sbuff_set(in, &s_m);         /* backtrack */
                fr_sbuff_marker_release(&s_m);
 
-               return xlat_tokenize_attribute(head, out, in, &attr_p_rules, t_rules);
+               return xlat_tokenize_attribute(head, in, &attr_p_rules, t_rules);
 
        /*
         *      Hint token was whitespace
@@ -832,28 +808,6 @@ int xlat_tokenize_expansion(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t
        return -1;
 }
 
-/*
- *     Temporary things until we swap to using tlists
- */
-#define xlat_exp_append(_tail, _node) do { \
-       *_tail = _node; \
-       _tail = &(_node)->next; \
-       _node = NULL; \
-       } while (0)
-
-static void xlat_exp_list_free(xlat_exp_t **head)
-{
-       xlat_exp_t *node = *head;
-
-       while (node) {
-               xlat_exp_t *next = node->next;
-               talloc_free(node);
-               node = next;
-       }
-
-       *head = NULL;
-}
-
 /** Parse an xlat string i.e. a non-expansion or non-function
  *
  * When this function is being used outside of an xlat expansion, i.e. on a string
@@ -904,10 +858,6 @@ static int xlat_tokenize_string(xlat_exp_head_t *head,
                                        );
        fr_sbuff_term_t                 *tokens;
        fr_sbuff_unescape_rules_t const *escapes;
-       xlat_exp_t                      *first, **tail;
-
-       first = NULL;
-       tail = &first;
 
        XLAT_DEBUG("STRING <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
 
@@ -940,11 +890,11 @@ static int xlat_tokenize_string(xlat_exp_head_t *head,
                                   escapes ? escapes->name : "(none)",
                                   fr_box_strvalue_len(str, talloc_array_length(str) - 1));
                        XLAT_HEXDUMP((uint8_t const *)str, talloc_array_length(str) - 1, " VALUE-BOX ");
-                       xlat_exp_append(tail, node);
+
+                       xlat_exp_insert_tail(head, node);
                } else if (slen < 0) {
                error:
                        talloc_free(node);
-                       xlat_exp_list_free(&first);
 
                        /*
                         *      Free our temporary array of terminals
@@ -956,8 +906,7 @@ static int xlat_tokenize_string(xlat_exp_head_t *head,
                if (fr_sbuff_adv_past_str_literal(in, "%{")) {
                        if (slen == 0) TALLOC_FREE(node); /* Free the empty node */
 
-                       if (xlat_tokenize_expansion(head, &node, in, t_rules) < 0) goto error;
-                       xlat_exp_append(tail, node);
+                       if (xlat_tokenize_expansion(head, in, t_rules) < 0) goto error;
                        continue;
                }
 
@@ -967,8 +916,7 @@ static int xlat_tokenize_string(xlat_exp_head_t *head,
                if (fr_sbuff_adv_past_str_literal(in, "%(")) {
                        if (slen == 0) TALLOC_FREE(node); /* Free the empty node */
 
-                       if (xlat_tokenize_function_args(head, &node, in, t_rules) < 0) goto error;
-                       xlat_exp_append(tail, node);
+                       if (xlat_tokenize_function_args(head, in, t_rules) < 0) goto error;
                        continue;
                }
 
@@ -992,8 +940,7 @@ static int xlat_tokenize_string(xlat_exp_head_t *head,
                        node->flags.pure = true;         /* value boxes are always pure */
                        node->flags.needs_async = false; /* value boxes are always non-async */
 
-                       xlat_flags_merge(&head->flags, &node->flags);
-                       xlat_exp_append(tail, node);
+                       xlat_exp_insert_tail(head, node);
                        continue;
                }
 
@@ -1012,7 +959,7 @@ static int xlat_tokenize_string(xlat_exp_head_t *head,
                 *      Empty input, emit no arguments
                 */
                } else if (slen == 0) {
-                       talloc_free(node);
+                       TALLOC_FREE(node);
                        XLAT_DEBUG("VALUE-BOX <-- (empty)");
                }
                break;
@@ -1022,8 +969,7 @@ static int xlat_tokenize_string(xlat_exp_head_t *head,
         *      Free our temporary array of terminals
         */
        if (tokens != &expansions) talloc_free(tokens);
-
-       head->next = first;
+       
        return 0;
 }
 
@@ -1338,7 +1284,7 @@ ssize_t xlat_tokenize_ephemeral(TALLOC_CTX *ctx, xlat_exp_head_t **out,
        /*
         *      Zero length expansion, return a zero length node.
         */
-       if (!head->next) {
+       if (!xlat_exp_head(head)) {
                *out = head;
                return 0;
        }
@@ -1380,11 +1326,9 @@ ssize_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *i
        fr_sbuff_marker_t               m;
        fr_sbuff_parse_rules_t const    *our_p_rules;   /* Bareword parse rules */
        fr_sbuff_parse_rules_t          tmp_p_rules;
-       xlat_exp_t                      **tail;
        xlat_exp_head_t                 *head;
 
        MEM(head = xlat_exp_head_alloc(ctx));
-       tail = &head->next;
 
        if (p_rules && p_rules->terminals) {
                tmp_p_rules = (fr_sbuff_parse_rules_t){ /* Stack allocated due to CL scope */
@@ -1457,7 +1401,7 @@ ssize_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *i
                        char            *str;
                        xlat_exp_t      *child;
 
-                       node->group->next = child = xlat_exp_alloc_null(node->group);
+                       child = xlat_exp_alloc_null(node->group);
                        xlat_exp_set_type(child, XLAT_BOX);
 
                        slen = fr_sbuff_out_aunescape_until(child, &str, &our_in, SIZE_MAX,
@@ -1467,6 +1411,7 @@ ssize_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *i
 
                        xlat_exp_set_name_buffer_shallow(child, str);
                        fr_value_box_strdup_shallow(&child->data, NULL, str, false);
+                       xlat_exp_insert_tail(node->group, child);
                }
                        break;
 
@@ -1492,8 +1437,8 @@ ssize_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *i
                xlat_exp_set_name_buffer_shallow(node, fmt);
 
                node->flags = node->group->flags;
-               xlat_flags_merge(&head->flags, &node->flags);
-               xlat_exp_append(tail, node);
+
+               xlat_exp_insert_tail(head, node);
 
                /*
                 *      If we're not and the end of the string
@@ -1917,8 +1862,7 @@ int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t **out, tmpl_t **vpt_p)
        node->vpt = talloc_move(node, vpt_p);
 
 done:
-       head->next = node;
-       head->flags = node->flags;
+       xlat_exp_insert_tail(head, node);
 
        *out = head;
        return 0;
@@ -1935,7 +1879,6 @@ done:
  */
 int xlat_copy(TALLOC_CTX *ctx, xlat_exp_head_t **out, xlat_exp_head_t const *in)
 {
-       xlat_exp_t **tail;
        xlat_exp_head_t *head;
 
        if (!in) {
@@ -1945,7 +1888,6 @@ int xlat_copy(TALLOC_CTX *ctx, xlat_exp_head_t **out, xlat_exp_head_t const *in)
 
        head = xlat_exp_head_alloc(ctx);
        head->flags = in->flags;
-       tail = &head->next;
 
        /*
         *      Copy everything in the list of nodes
@@ -2010,7 +1952,7 @@ int xlat_copy(TALLOC_CTX *ctx, xlat_exp_head_t **out, xlat_exp_head_t const *in)
                        break;
                }
 
-               xlat_exp_append(tail, node);
+               xlat_exp_insert_tail(head, node);
        }
 
        *out = head;