]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
reorganise and rename tmpl_cursor functions
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 8 Dec 2021 18:47:10 +0000 (13:47 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 8 Dec 2021 18:47:10 +0000 (13:47 -0500)
15 files changed:
src/lib/server/cond_eval.c
src/lib/server/libfreeradius-server.mk
src/lib/server/map.c
src/lib/server/map_async.c
src/lib/server/tmpl.h
src/lib/server/tmpl_dcursor.c [new file with mode: 0644]
src/lib/server/tmpl_dcursor.h [new file with mode: 0644]
src/lib/server/tmpl_eval.c
src/lib/unlang/subrequest.c
src/lib/unlang/xlat_builtin.c
src/lib/unlang/xlat_eval.c
src/lib/util/edit.c
src/modules/rlm_expr/rlm_expr.c
src/modules/rlm_linelog/rlm_linelog.c
src/modules/rlm_smtp/rlm_smtp.c

index 443249cf26e7558ad6071d75e3c5dd51a5731033..26027146d9e00246f22634d7db80ad72e79115e8 100644 (file)
  */
 RCSID("$Id$")
 
-#include <freeradius-devel/server/cond_eval.h>
 #include <freeradius-devel/server/cond.h>
+#include <freeradius-devel/server/cond_eval.h>
 #include <freeradius-devel/server/module.h>
 #include <freeradius-devel/server/paircmp.h>
 #include <freeradius-devel/server/regex.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/print.h>
 
@@ -522,7 +523,7 @@ static bool cond_compare_attrs(request_t *request, fr_value_box_t *lhs, map_t co
        int                     rcode;
        fr_pair_t               *vp;
        fr_dcursor_t            cursor;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
        fr_value_box_t          *rhs, rhs_cast;
        fr_dict_attr_t const    *da = NULL;
 
@@ -532,7 +533,7 @@ static bool cond_compare_attrs(request_t *request, fr_value_box_t *lhs, map_t co
        rhs = NULL;             /* shut up clang scan */
        fr_value_box_init_null(&rhs_cast);
 
-       for (vp = tmpl_pair_cursor_init(&rcode, request, &cc, &cursor, request, map->rhs);
+       for (vp = tmpl_dcursor_init(&rcode, request, &cc, &cursor, request, map->rhs);
             vp;
             vp = fr_dcursor_next(&cursor)) {
                if (cond_realize_attr(request, &rhs, &rhs_cast, map->rhs, vp, da) < 0) {
@@ -550,7 +551,7 @@ static bool cond_compare_attrs(request_t *request, fr_value_box_t *lhs, map_t co
                if (rcode != 0) break;
        }
 
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        return (rcode == 1);
 }
 
@@ -560,7 +561,7 @@ static bool cond_compare_virtual(request_t *request, map_t const *map)
        fr_pair_t               *virt, *vp;
        fr_value_box_t          *rhs, rhs_cast;
        fr_dcursor_t            cursor;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
 
        fr_assert(tmpl_is_attr(map->lhs));
        fr_assert(tmpl_is_attr(map->rhs));
@@ -568,7 +569,7 @@ static bool cond_compare_virtual(request_t *request, map_t const *map)
        rhs = NULL;             /* shut up clang scan */
        fr_value_box_clear(&rhs_cast);
 
-       for (vp = tmpl_pair_cursor_init(&rcode, request, &cc, &cursor, request, map->rhs);
+       for (vp = tmpl_dcursor_init(&rcode, request, &cc, &cursor, request, map->rhs);
             vp;
             vp = fr_dcursor_next(&cursor)) {
                if (cond_realize_attr(request, &rhs, &rhs_cast, map->rhs, vp, NULL) < 0) {
@@ -592,7 +593,7 @@ static bool cond_compare_virtual(request_t *request, map_t const *map)
                if (rcode != 0) break;
        }
 
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        return (rcode == 1);
 }
 
@@ -756,11 +757,11 @@ check_attrs:
        {
                fr_pair_t               *vp;
                fr_dcursor_t            cursor;
-               tmpl_pair_cursor_ctx_t  cc;
+               tmpl_dcursor_ctx_t      cc;
 
                fr_assert(!lhs);
 
-               for (vp = tmpl_pair_cursor_init(&rcode, request, &cc, &cursor, request, map->lhs);
+               for (vp = tmpl_dcursor_init(&rcode, request, &cc, &cursor, request, map->lhs);
                     vp;
                     vp = fr_dcursor_next(&cursor)) {
                        fr_value_box_t lhs_cast;
@@ -822,7 +823,7 @@ check_attrs:
                        continue;
                }
 
-               tmpl_pair_cursor_clear(&cc);
+               tmpl_dursor_clear(&cc);
        }
                break;
 
index abb74e4393077105f45a9e2d831e169d1d1a655e..3e6501cc85d115d72c26966653fdaee1e6f1aff8 100644 (file)
@@ -35,6 +35,7 @@ SOURCES       := \
        snmp.c \
        state.c \
        stats.c \
+       tmpl_dcursor.c \
        tmpl_eval.c \
        tmpl_tokenize.c \
        trigger.c \
index 3804da5d2bbc5b772cba633cf83c0ffc5f95b449..d932a4cffb55bb2ff40c4f8e2492da7eed7fbbf9 100644 (file)
 
 RCSID("$Id$")
 
+#include <freeradius-devel/server/cond.h>
 #include <freeradius-devel/server/exec.h>
 #include <freeradius-devel/server/exec_legacy.h>
 #include <freeradius-devel/server/map.h>
 #include <freeradius-devel/server/paircmp.h>
-#include <freeradius-devel/server/cond.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/base16.h>
@@ -1416,7 +1417,7 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
        tmpl_request_ref_t      request_ref;
        tmpl_pair_list_t        list_ref;
 
-       tmpl_pair_cursor_ctx_t  cc = {};
+       tmpl_dcursor_ctx_t      cc = {};
 
        fr_pair_list_init(&src_list);
        MAP_VERIFY(map);
@@ -1601,7 +1602,7 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
         *      the dst_list and vp pointing to the attribute or the VP
         *      being NULL (no attribute at that index).
         */
-       dst = tmpl_pair_cursor_init(NULL, tmp_ctx, &cc, &dst_list, request, map->lhs);
+       dst = tmpl_dcursor_init(NULL, tmp_ctx, &cc, &dst_list, request, map->lhs);
        /*
         *      The destination is an attribute
         */
@@ -1723,7 +1724,7 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
                 *      Find out what we need to build and build it
                 */
                if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
-                   (tmpl_extents_build_to_leaf(&leaf, &interior, map->lhs) < 0)) {
+                   (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
                        fr_dlist_talloc_free(&leaf);
                        fr_dlist_talloc_free(&interior);
                        rcode = -1;
@@ -1786,7 +1787,7 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
                 *      Find out what we need to build and build it
                 */
                if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
-                   (tmpl_extents_build_to_leaf(&leaf, &interior, map->lhs) < 0)) {
+                   (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
                    op_set_error:
                        fr_dlist_talloc_free(&leaf);
                        fr_dlist_talloc_free(&interior);
@@ -1836,7 +1837,7 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
                 *      Find out what we need to build and build it
                 */
                if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
-                   (tmpl_extents_build_to_leaf(&leaf, &interior, map->lhs) < 0)) {
+                   (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
                        fr_dlist_talloc_free(&leaf);
                        fr_dlist_talloc_free(&interior);
                        rcode = -1;
@@ -1913,7 +1914,7 @@ update:
        fr_assert(fr_pair_list_empty(&src_list));
 
 finish:
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        talloc_free(tmp_ctx);
        return rcode;
 }
index 2a57499b5b7523ecdfd950bf7b6591538f56000a..4eed8a3c7f37747429fb83998dda3a1a408bcb1b 100644 (file)
@@ -31,8 +31,9 @@ RCSID("$Id$")
 #include <freeradius-devel/server/exec.h>
 #include <freeradius-devel/server/map.h>
 #include <freeradius-devel/server/paircmp.h>
-#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 
+#include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/misc.h>
 #include <freeradius-devel/util/pair_legacy.h>
 
@@ -538,7 +539,7 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
        case TMPL_TYPE_ATTR:
        {
                fr_dcursor_t            from;
-               tmpl_pair_cursor_ctx_t  cc_attr;
+               tmpl_dcursor_ctx_t      cc_attr;
                fr_pair_t               *vp;
                fr_value_box_t          *n_vb;
                int                     err;
@@ -556,7 +557,7 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                 *      Check we have pairs to copy *before*
                 *      doing any expensive allocations.
                 */
-               vp = tmpl_pair_cursor_init(&err, request, &cc_attr, &from, request, mutated->rhs);
+               vp = tmpl_dcursor_init(&err, request, &cc_attr, &from, request, mutated->rhs);
                if (!vp) switch (err) {
                default:
                        break;
@@ -568,20 +569,20 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                         *      we should delete all LHS attributes.
                         */
                        if (mutated->op == T_OP_SET) n = list_mod_delete_afrom_map(ctx, original, mutated);
-                       tmpl_pair_cursor_clear(&cc_attr);
+                       tmpl_dursor_clear(&cc_attr);
                        goto finish;
 
                case -2:                /* No matching list */
                case -3:                /* No request context */
                case -4:                /* memory allocation error */
                        RPEDEBUG("Failed resolving attribute source");
-                       tmpl_pair_cursor_clear(&cc_attr);
+                       tmpl_dursor_clear(&cc_attr);
                        goto error;
                }
 
                n = list_mod_generic_afrom_map(ctx, original, mutated);
                if (!n) {
-                       tmpl_pair_cursor_clear(&cc_attr);
+                       tmpl_dursor_clear(&cc_attr);
                        goto error;
                }
 
@@ -593,7 +594,7 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                        attr_error:
                                fr_dcursor_head(&values);
                                fr_dcursor_free_list(&values);
-                               tmpl_pair_cursor_clear(&cc_attr);
+                               tmpl_dursor_clear(&cc_attr);
                                goto error;
                        }
 
@@ -611,7 +612,7 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                        fr_dcursor_append(&values, n_vb);
                } while ((vp = fr_dcursor_next(&from)));
 
-               tmpl_pair_cursor_clear(&cc_attr);
+               tmpl_dursor_clear(&cc_attr);
        }
                break;
 
@@ -950,7 +951,7 @@ int map_list_mod_apply(request_t *request, vp_list_mod_t const *vlm)
        TALLOC_CTX              *parent;
 
        fr_dcursor_t            list;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
 
        memset(&cc, 0, sizeof(cc));
 
@@ -1088,7 +1089,7 @@ int map_list_mod_apply(request_t *request, vp_list_mod_t const *vlm)
         *      the list and vp pointing to the attribute or the VP
         *      being NULL (no attribute at that index).
         */
-       found = tmpl_pair_cursor_init(NULL, request, &cc, &list, request, mod->lhs);
+       found = tmpl_dcursor_init(NULL, request, &cc, &list, request, mod->lhs);
 
        /*
         *      The destination is an attribute
@@ -1286,6 +1287,6 @@ int map_list_mod_apply(request_t *request, vp_list_mod_t const *vlm)
        }
 
 finish:
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        return rcode;
 }
index f572edcc9070252708f73a7e7f0e017190ccfc11..0dfae50397e7e28e3bf43dd7c5b44b634ac71729 100644 (file)
@@ -45,7 +45,7 @@
  * functions which can be used to iterate over only the #fr_pair_t that match a
  * tmpl_t in a given list.
  *
- * @see tmpl_pair_cursor_init
+ * @see tmpl_dcursor_init
  * @see tmpl_cursor_next
  *
  * Or for simplicity, there are functions which wrap the cursor functions, to copy or
@@ -526,57 +526,6 @@ struct tmpl_s {
                                                ///< they ensure the correct parsing rules are applied.
 };
 
-typedef struct tmpl_cursor_ctx_s tmpl_pair_cursor_ctx_t;
-typedef struct tmpl_cursor_nested_s tmpl_cursor_nested_t;
-
-typedef fr_pair_t *(*tmpl_cursor_eval_t)(fr_dlist_head_t *list_head, fr_pair_t *current, tmpl_cursor_nested_t *ns);
-
-/** State for traversing an attribute reference
- *
- */
-struct tmpl_cursor_nested_s {
-       fr_dlist_t              entry;          //!< Entry in the dlist.
-       tmpl_attr_t const       *ar;            //!< Attribute reference this state
-                                               ///< entry is associated with.  Mainly for debugging.
-       tmpl_cursor_eval_t      func;           //!< Function used to evaluate this attribute reference.
-       TALLOC_CTX              *list_ctx;      //!< Track where we should be allocating attributes.
-
-       union {
-               struct {
-                       fr_dcursor_t            cursor;                 //!< Group traversal is much easier
-                                                                       ///< but we still need to keep track
-                                                                       ///< where we are in the list in case
-                                                                       ///< we're doing counts.
-               } group;
-
-               struct {
-                       fr_pair_list_t          *list_head;             //!< Head of the list we're currently
-                                                                       ///< iterating over.
-               } leaf;
-       };
-};
-
-/** Maintains state between cursor calls
- *
- */
-struct tmpl_cursor_ctx_s {
-       TALLOC_CTX              *ctx;           //!< Temporary allocations go here.
-       TALLOC_CTX              *pool;          //!< Temporary pool.
-       tmpl_t const            *vpt;           //!< tmpl we're evaluating.
-
-       request_t               *request;       //!< Result of following the request references.
-       fr_pair_list_t          *list;          //!< List within the request.
-
-       tmpl_cursor_nested_t    leaf;           //!< Pre-allocated leaf state.  We always need
-                                               ///< one of these so it doesn't make sense to
-                                               ///< allocate it later.
-
-       fr_dlist_head_t         nested;         //!< Nested state.  These are allocated when we
-                                               ///< need to maintain state between multiple
-                                               ///< cursor calls for a particular attribute
-                                               ///< reference.
-};
-
 /** Describes the current extents of a pair tree in relation to the tree described by a tmpl_t
  *
  */
@@ -735,21 +684,21 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt);
  @code{.c}
    static tmpl_t     list = tmpl_init_initialiser_list(CURRENT_REQUEST, PAIR_LIST_REQUEST);
    fr_dcursor_t      cursor;
-   tmpl_pair_cursor_ctx_t cc,
+   tmpl_dcursor_ctx_t cc,
    fr_pair_t        *vp;
 
    // Iterate over all pairs in the request list
-   for (vp = tmpl_pair_cursor_init(NULL, &cursor, request, &list);
+   for (vp = tmpl_dcursor_init(NULL, &cursor, request, &list);
        vp;
        vp = tmpl_cursor_next(&cursor, &list)) {
        // Do something
    }
-   tmpl_pair_cursor_clear(&cc);
+   tmpl_dursor_clear(&cc);
  @endcode
  *
  * @param _request to locate the list in.
  * @param _list to set as the target for the template.
- * @see tmpl_pair_cursor_init
+ * @see tmpl_dcursor_init
  * @see tmpl_cursor_next
  */
 #define        tmpl_init_initialiser_list(_request, _list)\
@@ -1012,12 +961,6 @@ ssize_t                   _tmpl_to_atype(TALLOC_CTX *ctx, void *out,
                                       fr_type_t dst_type)
                        CC_HINT(nonnull (2, 3, 4));
 
-fr_pair_t              *tmpl_pair_cursor_init(int *err, TALLOC_CTX *ctx, tmpl_pair_cursor_ctx_t *cc,
-                                         fr_dcursor_t *cursor, request_t *request,
-                                         tmpl_t const *vpt);
-
-void                   tmpl_pair_cursor_clear(tmpl_pair_cursor_ctx_t *cc);
-
 int                    tmpl_copy_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out,
                                        request_t *request, tmpl_t const *vpt);
 
@@ -1032,7 +975,7 @@ int                        tmpl_extents_find(TALLOC_CTX *ctx,
                                          fr_dlist_head_t *leaf, fr_dlist_head_t *interior,
                                          request_t *request, tmpl_t const *vpt);
 
-int                    tmpl_extents_build_to_leaf(fr_dlist_head_t *leaf, fr_dlist_head_t *interior,
+int                    tmpl_extents_build_to_leaf_parent(fr_dlist_head_t *leaf, fr_dlist_head_t *interior,
                                                   tmpl_t const *vpt);
 
 void                   tmpl_extents_debug(fr_dlist_head_t *head);
diff --git a/src/lib/server/tmpl_dcursor.c b/src/lib/server/tmpl_dcursor.c
new file mode 100644 (file)
index 0000000..b9f9abc
--- /dev/null
@@ -0,0 +1,722 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @brief #fr_pair_t template functions
+ * @file src/lib/server/tmpl_cursor.c
+ *
+ * @ingroup AVP
+ *
+ * @copyright 2020-2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/server/exec.h>
+#include <freeradius-devel/server/exec_legacy.h>
+#include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
+#include <freeradius-devel/util/dlist.h>
+#include <freeradius-devel/util/proto.h>
+#include <freeradius-devel/util/value.h>
+#include <freeradius-devel/util/edit.h>
+
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_pool_init(tmpl_dcursor_ctx_t *cc)
+{
+       if (!cc->pool) MEM(cc->pool = talloc_pool(cc->ctx, sizeof(tmpl_dcursor_nested_t) * 5));
+}
+
+/** Traverse a group attribute
+ *
+ * Here we just look for a particular group attribute in the context of its parent
+ *
+ * @param[in] list_head The head of the pair_list being evaluated.
+ * @param[in] current  The pair to evaluate.
+ * @param[in] ns       Tracks tree position between cursor calls.
+ * @return
+ *     - the next matching attribute
+ *     - NULL if none found
+ */
+static fr_pair_t *_tmpl_cursor_child_eval(UNUSED fr_dlist_head_t *list_head, UNUSED fr_pair_t *current, tmpl_dcursor_nested_t *ns)
+{
+       fr_pair_t *vp;
+
+       for (vp = fr_dcursor_current(&ns->group.cursor);
+            vp;
+            vp = fr_dcursor_next(&ns->group.cursor)) {
+               if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) {
+                       fr_dcursor_next(&ns->group.cursor);     /* Advance to correct position for next call */
+                       return vp;
+               }
+       }
+
+       return NULL;
+}
+
+/** Initialise the evaluation context for traversing a group attribute
+ *
+ */
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_child_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
+{
+       tmpl_dcursor_nested_t *ns;
+
+       _tmpl_cursor_pool_init(cc);
+       MEM(ns = talloc(cc->pool, tmpl_dcursor_nested_t));
+       *ns = (tmpl_dcursor_nested_t){
+               .ar = ar,
+               .func = _tmpl_cursor_child_eval,
+               .list_ctx = list_ctx
+       };
+       fr_pair_dcursor_init(&ns->group.cursor, list);
+       fr_dlist_insert_tail(&cc->nested, ns);
+}
+
+/** Find a leaf attribute
+ *
+ * @param[in] list_head The head of the pair_list being evaluated.
+ * @param[in] curr     The current attribute to start searching from.
+ * @param[in] ns       Tracks tree position between cursor calls.
+ * @return
+ *     - the next matching attribute
+ *     - NULL if none found
+ */
+static fr_pair_t *_tmpl_cursor_leaf_eval(fr_dlist_head_t *list_head, fr_pair_t *curr, tmpl_dcursor_nested_t *ns)
+{
+       fr_pair_t *vp = curr;
+
+       while (vp) {
+               if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) return vp;
+               vp = fr_dlist_next(list_head, vp);
+       }
+
+       return NULL;
+}
+
+/** Initialise the evaluation context for finding a leaf attribute
+ *
+ */
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_leaf_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
+{
+       tmpl_dcursor_nested_t   *ns = &cc->leaf;
+
+       *ns = (tmpl_dcursor_nested_t){
+               .ar = ar,
+               .func = _tmpl_cursor_leaf_eval,
+               .list_ctx = list_ctx
+       };
+       ns->leaf.list_head = list;
+       fr_dlist_insert_tail(&cc->nested, ns);
+}
+
+/** Stub list eval function until we can remove lists
+ *
+ */
+static fr_pair_t *_tmpl_cursor_list_eval(UNUSED fr_dlist_head_t *list_head, fr_pair_t *curr, UNUSED tmpl_dcursor_nested_t *ns)
+{
+       return curr;
+}
+
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_list_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
+{
+       tmpl_dcursor_nested_t *ns;
+
+       ns = &cc->leaf;
+       *ns = (tmpl_dcursor_nested_t){
+               .ar = ar,
+               .func = _tmpl_cursor_list_eval,
+               .list_ctx = list_ctx
+       };
+       ns->leaf.list_head = list;
+       fr_dlist_insert_tail(&cc->nested, ns);
+}
+
+static inline CC_HINT(always_inline) void _tmpl_cursor_common_pop(tmpl_dcursor_ctx_t *cc)
+{
+       tmpl_dcursor_nested_t *ns = fr_dlist_pop_tail(&cc->nested);
+
+       if (ns != &cc->leaf) talloc_free(ns);
+}
+
+/** Evaluates, then, sometimes, pops evaluation contexts from the tmpl stack
+ *
+ * To pop or not to pop is determined by whether evaluating the context again
+ * would/should/could produce another fr_pair_t.
+ *
+ * @param[in] list_head The head of the pair_list being evaluated.
+ * @param[in] curr     The pair to evaluate.
+ * @param[in] cc       Tracks state between cursor calls.
+ * @return the vp evaluated.
+ */
+static inline CC_HINT(always_inline)
+fr_pair_t *_tmpl_cursor_eval(fr_dlist_head_t *list_head, fr_pair_t *curr, tmpl_dcursor_ctx_t *cc)
+{
+       tmpl_attr_t const       *ar;
+       tmpl_dcursor_nested_t   *ns;
+       fr_pair_t               *iter = curr, *vp;
+
+       ns = fr_dlist_tail(&cc->nested);
+       ar = ns->ar;
+
+       if (ar) switch (ar->ar_num) {
+       /*
+        *      Get the first instance
+        */
+       case NUM_ANY:
+               vp = ns->func(list_head, curr, ns);
+               _tmpl_cursor_common_pop(cc);
+               break;
+
+       /*
+        *      Get all instances
+        */
+       case NUM_ALL:
+       case NUM_COUNT:
+       all_inst:
+               vp = ns->func(list_head, curr, ns);
+               if (!vp) _tmpl_cursor_common_pop(cc);   /* pop only when we're done */
+               break;
+
+       /*
+        *      Get the last instance
+        */
+       case NUM_LAST:
+               vp = NULL;
+               while ((iter = ns->func(list_head, iter, ns))) {
+                       vp = iter;
+
+                       if (!fr_dlist_next(list_head, vp)) break;
+
+                       iter = fr_dlist_next(list_head,vp);
+               }
+               _tmpl_cursor_common_pop(cc);
+               break;
+
+       /*
+        *      Get the n'th instance
+        */
+       default:
+       {
+               int16_t         i = 0;
+
+               for (;;) {
+                       vp = ns->func(list_head, iter, ns);
+                       if (!vp) break; /* Prev and next at the correct points */
+
+                       if (++i > ar->num) break;
+
+                       iter = fr_dlist_next(list_head, vp);
+               };
+               _tmpl_cursor_common_pop(cc);
+       }
+               break;
+       } else goto all_inst;   /* Used for TMPL_TYPE_LIST */
+
+       return vp;
+}
+
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_pair_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
+{
+       if (fr_dlist_next(&cc->vpt->data.attribute.ar, ar)) switch (ar->ar_da->type) {
+       case FR_TYPE_STRUCTURAL:
+               _tmpl_cursor_child_init(list_ctx, list, ar, cc);
+               break;
+
+       default:
+       leaf:
+               _tmpl_cursor_leaf_init(list_ctx, list, ar, cc);
+               break;
+       } else goto leaf;
+}
+
+static void *_tmpl_cursor_next(fr_dlist_head_t *list, void *curr, void *uctx)
+{
+       tmpl_dcursor_ctx_t      *cc = uctx;
+       tmpl_t const            *vpt = cc->vpt;
+
+       fr_pair_t               *vp;
+       fr_pair_list_t          *list_head;
+
+       switch (vpt->type) {
+       case TMPL_TYPE_ATTR:
+       {
+               tmpl_attr_t const       *ar = NULL;
+               tmpl_dcursor_nested_t   *ns = NULL;
+
+               /*
+                *      - Continue until there are no evaluation contexts
+                *      - Push a evaluation context if evaluating the head of the
+                *        stack yields a VP and we're not at the deepest attribute
+                *        reference.
+                *      - Return if we have a VP and there are no more attribute
+                *        references to push, i.e. we're at the deepest attribute
+                *        reference.
+                */
+               while ((ns = fr_dlist_tail(&cc->nested))) {
+                       ar = ns->ar;
+                       vp = _tmpl_cursor_eval(list, curr, cc);
+                       if (!vp) continue;
+
+                       ar = fr_dlist_next(&vpt->data.attribute.ar, ar);
+                       if (ar) {
+                               list_head = &vp->vp_group;
+                               _tmpl_cursor_pair_init(vp, list_head, ar, cc);
+                               curr = fr_pair_list_head(list_head);
+                               list = UNCONST(fr_dlist_head_t *, fr_pair_list_order(list_head));
+                               continue;
+                       }
+
+                       return vp;
+               }
+
+       null_result:
+               return NULL;
+       }
+
+       /*
+        *      Hacks for evaluating lists
+        *      Hopefully this tmpl type goes away soon...
+        */
+       case TMPL_TYPE_LIST:
+               if (!fr_dlist_tail(&cc->nested)) goto null_result;      /* end of list */
+
+               vp = _tmpl_cursor_eval(list, curr, cc);
+               if (!vp) goto null_result;
+
+               return vp;
+
+       default:
+               fr_assert(0);
+       }
+
+       return NULL;
+}
+
+/** Initialise a #fr_dcursor_t to the #fr_pair_t specified by a #tmpl_t
+ *
+ * This makes iterating over the one or more #fr_pair_t specified by a #tmpl_t
+ * significantly easier.
+ *
+ * @param[out] err             May be NULL if no error code is required.
+ *                             Will be set to:
+ *                             - 0 on success.
+ *                             - -1 if no matching #fr_pair_t could be found.
+ *                             - -2 if list could not be found (doesn't exist in current #request_t).
+ *                             - -3 if context could not be found (no parent #request_t available).
+ * @param[in] ctx              to make temporary allocations under.
+ * @param[in] cc               to initialise.  Tracks evaluation state.
+ *                             Must be explicitly cleared with tmpl_cursor_state_clear
+ *                             otherwise we will leak memory.
+ * @param[in] cursor           to store iterator position.
+ * @param[in] request          The current #request_t.
+ * @param[in] vpt              specifying the #fr_pair_t type or list to iterate over.
+ * @return
+ *     - First #fr_pair_t specified by the #tmpl_t.
+ *     - NULL if no matching #fr_pair_t found, and NULL on error.
+ *
+ * @see tmpl_cursor_next
+ */
+fr_pair_t *tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc,
+                                fr_dcursor_t *cursor, request_t *request, tmpl_t const *vpt)
+{
+       fr_pair_t               *vp = NULL;
+       fr_pair_list_t          *list_head;
+       tmpl_request_t          *rr = NULL;
+       TALLOC_CTX              *list_ctx;
+
+       TMPL_VERIFY(vpt);
+
+       fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+
+       if (err) *err = 0;
+
+       /*
+        *      Navigate to the correct request context
+        */
+       while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
+               if (tmpl_request_ptr(&request, rr->request) < 0) {
+                       if (err) {
+                               *err = -3;
+                               fr_strerror_printf("Request context \"%s\" not available",
+                                                  fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
+                       }
+               error:
+                       memset(cc, 0, sizeof(*cc));     /* so tmpl_dursor_clear doesn't explode */
+                       return NULL;
+               }
+       }
+
+       /*
+        *      Get the right list in the specified context
+        */
+       list_head = tmpl_list_head(request, tmpl_list(vpt));
+       if (!list_head) {
+               if (err) {
+                       *err = -2;
+                       fr_strerror_printf("List \"%s\" not available in this context",
+                                          fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
+               }
+               goto error;
+       }
+       list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
+
+       /*
+        *      Initialise the temporary cursor context
+        */
+       *cc = (tmpl_dcursor_ctx_t){
+               .vpt = vpt,
+               .ctx = ctx,
+               .request = request,
+               .list = list_head
+       };
+       fr_dlist_init(&cc->nested, tmpl_dcursor_nested_t, entry);
+
+       /*
+        *      Prime the stack!
+        */
+       switch (vpt->type) {
+       case TMPL_TYPE_ATTR:
+               _tmpl_cursor_pair_init(list_ctx, cc->list, fr_dlist_head(&vpt->data.attribute.ar), cc);
+               break;
+
+       case TMPL_TYPE_LIST:
+               _tmpl_cursor_list_init(list_ctx, cc->list, fr_dlist_head(&vpt->data.attribute.ar), cc);
+               break;
+
+       default:
+               fr_assert(0);
+               break;
+       }
+
+       /*
+        *      Get the first entry from the tmpl
+        */
+       vp = fr_pair_dcursor_iter_init(cursor, list_head, _tmpl_cursor_next, cc);
+       if (!vp) {
+               if (err) {
+                       *err = -1;
+                       if (tmpl_is_list(vpt)) {
+                               fr_strerror_printf("List \"%s\" is empty", vpt->name);
+                       } else {
+                               fr_strerror_printf("No matching \"%s\" pairs found", tmpl_da(vpt)->name);
+                       }
+               }
+               return NULL;
+       }
+
+       return vp;
+}
+
+/** Clear any temporary state allocations
+ *
+ */
+void tmpl_dursor_clear(tmpl_dcursor_ctx_t *cc)
+{
+       if (!fr_dlist_num_elements(&cc->nested)) return;/* Help simplify dealing with unused cursor ctxs */
+
+       fr_dlist_remove(&cc->nested, &cc->leaf);        /* Noop if leaf isn't inserted */
+       fr_dlist_talloc_free(&cc->nested);
+
+       /*
+        *      Always free the pool because it's allocated when
+        *      any nested ctxs are used.
+        */
+       TALLOC_FREE(cc->pool);
+}
+
+
+#define EXTENT_ADD(_out, _ar, _list_ctx, _list) \
+       do { \
+               tmpl_attr_extent_t      *_extent; \
+               MEM(_extent = talloc(ctx, tmpl_attr_extent_t)); \
+               *_extent = (tmpl_attr_extent_t){ \
+                       .ar = _ar,      \
+                       .list_ctx = _list_ctx, \
+                       .list = _list   \
+               }; \
+               fr_dlist_insert_tail(_out, _extent); \
+       } while (0)
+
+/** Determines points where the reference list extends beyond the current pair tree
+ *
+ * If a particular branch in the VP hierarchy is incomplete, i.e. the chain of attribute
+ * refers to nodes deeper than the nodes currently in the tree, then we return the
+ * deepest point node in the tree which matched, and the ar that we failed to evaluate.
+ *
+ * If the reference list resolves to one or more structural pairs, return those as well.
+ *
+ * This function can be used for a number of different operations, but it's most useful
+ * for determining insertion points for new attributes, or determining which attributes
+ * need to be updated.
+ *
+ * @param[in] ctx                              to allocate.  It's recommended to pass a pool with space
+ *                                             for at least five extent structures.
+ * @param[out] existing                                List of extents we discovered by evaluating all
+ *                                             attribute references. May be NULL.
+ * @param[out] to_build                        List of extents that need building out, i.e. references
+ *                                             extend beyond pairs. May be NULL.
+ * @param[in] request                          The current #request_t.
+ * @param[in] vpt                              specifying the #fr_pair_t type to retrieve or create.
+ *                                             Must be #TMPL_TYPE_ATTR.
+ * @return
+ *     - 0 on success a pair was found.
+ *     - -2 if list could not be found (doesn't exist in current #request_t).
+ *     - -3 if context could not be found (no parent #request_t available).
+ */
+int tmpl_extents_find(TALLOC_CTX *ctx,
+                     fr_dlist_head_t *existing, fr_dlist_head_t *to_build,
+                     request_t *request, tmpl_t const *vpt)
+{
+       fr_pair_t               *curr = NULL;
+       fr_pair_list_t          *list_head;
+
+       TALLOC_CTX              *list_ctx = NULL;
+
+       tmpl_dcursor_ctx_t      cc;
+       tmpl_dcursor_nested_t   *ns = NULL;
+
+       tmpl_request_t          *rr = NULL;
+       tmpl_attr_t const       *ar = NULL;
+
+       TMPL_VERIFY(vpt);
+
+       fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+
+       /*
+        *      Navigate to the correct request context
+        */
+       while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
+               if (tmpl_request_ptr(&request, rr->request) < 0) {
+                       fr_strerror_printf("Request context \"%s\" not available",
+                                          fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
+                       return -3;
+               }
+       }
+
+       /*
+        *      Get the right list in the specified context
+        */
+       list_head = tmpl_list_head(request, tmpl_list(vpt));
+       if (!list_head) {
+               fr_strerror_printf("List \"%s\" not available in this context",
+                                  fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
+               return -2;
+       }
+       list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
+
+       /*
+        *      If it's a list, just return the list head
+        */
+       if (vpt->type == TMPL_TYPE_LIST) {
+       do_list:
+               if (existing) EXTENT_ADD(existing, NULL, list_ctx, list_head);
+               return 0;
+       }
+
+       /*
+        *      If it's a leaf skip all the expensive
+        *      initialisation and just return the list
+        *      it's part of.
+        *
+        *      This is only needed because lists are
+        *      treated specially.  Once lists are groups
+        *      this can be removed.
+        */
+       ar = fr_dlist_head(&vpt->data.attribute.ar);
+       switch (ar->ar_da->type) {
+       case FR_TYPE_STRUCTURAL:
+               break;
+
+       default:
+               goto do_list;
+       }
+
+       /*
+        *      Initialise the temporary cursor context
+        */
+       cc = (tmpl_dcursor_ctx_t){
+               .vpt = vpt,
+               .ctx = ctx,
+               .request = request,
+               .list = list_head
+       };
+       fr_dlist_init(&cc.nested, tmpl_dcursor_nested_t, entry);
+
+       /*
+        *      Prime the stack!
+        */
+       _tmpl_cursor_pair_init(list_ctx, cc.list, fr_dlist_head(&vpt->data.attribute.ar), &cc);
+
+       /*
+        *      - Continue until there are no evaluation contexts
+        *      - Push a evaluation context if evaluating the head of the
+        *        stack yields a VP and we're not at the deepest attribute
+        *        reference.
+        *      - Return if we have a VP and there are no more attribute
+        *        references to push, i.e. we're at the deepest attribute
+        *        reference.
+        */
+       curr = fr_pair_list_head(list_head);
+       while ((ns = fr_dlist_tail(&cc.nested))) {
+               tmpl_attr_t const *n_ar;
+
+               list_ctx = ns->list_ctx;
+               ar = ns->ar;
+               curr = _tmpl_cursor_eval(&list_head->order, curr, &cc);
+               if (!curr) {
+                       /*
+                        *      References extend beyond current
+                        *      pair tree.
+                        */
+                       if (!ar->resolve_only && to_build) EXTENT_ADD(to_build, ar, list_ctx, list_head);
+                       continue;       /* Rely on _tmpl_cursor_eval popping the stack */
+               }
+
+               /*
+                *      Evaluate the next reference
+                */
+               n_ar = fr_dlist_next(&vpt->data.attribute.ar, ar);
+               if (n_ar) {
+                       ar = n_ar;
+                       list_head = &curr->vp_group;
+                       list_ctx = curr;        /* Allocations are under the group */
+                       _tmpl_cursor_pair_init(list_ctx, list_head, ar, &cc);
+                       curr = fr_pair_list_head(list_head);
+                       continue;
+               }
+
+               /*
+                *      Only reached when we can't find an exiting
+                *      part of the pair_root to keep walking.
+                *
+                *      VP tree may extend beyond the reference.
+                *      If the reference was structural, record this
+                *      as an extent.
+                */
+               if (existing) switch (ar->da->type) {
+               case FR_TYPE_STRUCTURAL:
+                       EXTENT_ADD(existing, NULL, curr, list_head);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+/** Allocate interior pairs
+ *
+ * Builds out the pair tree to the point where leaf attributes can be added
+ *
+ * @param[out] existing        List to add built out attributes to.
+ * @param[in] to_build List to remove attributes from.
+ * @param[in] vpt      We are evaluating.
+ * @return
+ *     - 0 on success.
+ *      - -1 on failure.
+ */
+int tmpl_extents_build_to_leaf_parent(fr_dlist_head_t *existing, fr_dlist_head_t *to_build, tmpl_t const *vpt)
+{
+       tmpl_attr_extent_t      *extent = NULL;
+
+       while ((extent = fr_dlist_head(to_build))) {
+               fr_pair_list_t          *list;
+               TALLOC_CTX              *list_ctx;
+               fr_pair_t               *vp;
+               tmpl_attr_t const       *ar;
+
+               fr_assert(extent->ar);  /* Interior extents MUST contain an ar */
+
+               /*
+                *      Try and allocate VPs for the
+                *      rest of the attribute references.
+                */
+               for (ar = extent->ar, list = extent->list, list_ctx = extent->list_ctx;
+                    ar;
+                    ar = fr_dlist_next(&vpt->data.attribute.ar, ar)) {
+                       switch (ar->type) {
+                       case TMPL_ATTR_TYPE_NORMAL:
+                       case TMPL_ATTR_TYPE_UNKNOWN:
+                               /*
+                                *      Don't build leaf attributes
+                                */
+                               if (!fr_type_is_structural(ar->ar_da->type)) continue;
+
+                               MEM(vp = fr_pair_afrom_da(list_ctx, ar->ar_da));        /* Copies unknowns */
+                               fr_pair_append(list, vp);
+                               list = &vp->vp_group;
+                               list_ctx = vp;          /* New allocations occur under the VP */
+                               break;
+
+                       default:
+                               fr_assert_fail("references of this type should have been resolved");
+                               return -1;
+                       }
+               }
+
+               fr_dlist_remove(to_build, extent);      /* Do this *before* zeroing the dlist headers */
+               *extent = (tmpl_attr_extent_t){
+                       .list = list,
+                       .list_ctx = list_ctx
+               };
+               fr_dlist_insert_tail(existing, extent); /* move between in and out */
+       }
+
+       return 0;
+}
+
+void tmpl_extents_debug(fr_dlist_head_t *head)
+{
+       tmpl_attr_extent_t const *extent = NULL;
+       fr_pair_t *vp = NULL;
+
+       for (extent = fr_dlist_head(head);
+            extent;
+            extent = fr_dlist_next(head, extent)) {
+               tmpl_attr_t const *ar = extent->ar;
+               char const *ctx_name;
+
+               if (ar) {
+                       FR_FAULT_LOG("extent-interior-attr");
+                       tmpl_attr_ref_debug(extent->ar, 0);
+               } else {
+                       FR_FAULT_LOG("extent-leaf");
+               }
+
+               ctx_name = talloc_get_name(extent->list_ctx);
+               if (strcmp(ctx_name, "fr_pair_t") == 0) {
+                       FR_FAULT_LOG("list_ctx     : %p (%s, %s)", extent->list_ctx, ctx_name,
+                                    ((fr_pair_t *)extent->list_ctx)->da->name);
+               } else {
+                       FR_FAULT_LOG("list_ctx     : %p (%s)", extent->list_ctx, ctx_name);
+               }
+               FR_FAULT_LOG("list         : %p", extent->list);
+               if (fr_pair_list_empty(extent->list)) {
+                       FR_FAULT_LOG("list (first) : none (%p)", extent->list);
+               } else {
+                       vp = fr_pair_list_head(extent->list);
+                       FR_FAULT_LOG("list (first) : %s (%p)", vp->da->name, extent->list);
+               }
+       }
+
+}
diff --git a/src/lib/server/tmpl_dcursor.h b/src/lib/server/tmpl_dcursor.h
new file mode 100644 (file)
index 0000000..c75cd06
--- /dev/null
@@ -0,0 +1,100 @@
+#pragma once
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+#include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/util/dcursor.h>
+
+RCSIDH(tmpl_dcursor_t, "$Id$")
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct tmpl_dcursor_ctx_s tmpl_dcursor_ctx_t;
+typedef struct tmpl_dcursor_nested_s tmpl_dcursor_nested_t;
+
+/** Evaluation function for iterating over a given type of attribute
+ *
+ * Currently attributes are divided into structural and leaf attributes,
+ * so we need an evaluation function for each of those.
+ *
+ * @param[in] list_head                to evaluate.
+ * @param[in] current          position in the list.
+ * @param[in] ns               Nested state.
+ */
+typedef fr_pair_t *(*tmpl_dursor_eval_t)(fr_dlist_head_t *list_head, fr_pair_t *current, tmpl_dcursor_nested_t *ns);
+
+/** State for traversing an attribute reference
+ *
+ */
+struct tmpl_dcursor_nested_s {
+       fr_dlist_t              entry;          //!< Entry in the dlist that forms the evaluation stack.
+       tmpl_attr_t const       *ar;            //!< Attribute reference this state
+                                               ///< entry is associated with.  Mainly for debugging.
+       tmpl_dursor_eval_t      func;           //!< Function used to evaluate this attribute reference.
+       TALLOC_CTX              *list_ctx;      //!< Track where we should be allocating attributes.
+
+       bool                    seen;           //!< Whether we've seen an attribute at this level of
+                                               ///< evaluation already.  This is mainly used where
+                                               ///< the build cursor is used.
+
+       union {
+               struct {
+                       fr_dcursor_t            cursor;                 //!< Group traversal is much easier
+                                                                       ///< but we still need to keep track
+                                                                       ///< where we are in the list in case
+                                                                       ///< we're doing counts.
+               } group;
+
+               struct {
+                       fr_pair_list_t          *list_head;             //!< Head of the list we're currently
+                                                                       ///< iterating over.
+               } leaf;
+       };
+};
+
+/** Maintains state between cursor calls
+ *
+ */
+struct tmpl_dcursor_ctx_s {
+       TALLOC_CTX              *ctx;           //!< Temporary allocations go here.
+       TALLOC_CTX              *pool;          //!< Temporary pool.
+       tmpl_t const            *vpt;           //!< tmpl we're evaluating.
+
+       request_t               *request;       //!< Result of following the request references.
+
+       fr_pair_list_t          *list;          //!< List within the request.
+
+       fr_dlist_head_t         nested;         //!< Nested state.  These are allocated when we
+                                               ///< need to maintain state between multiple
+                                               ///< cursor calls for a particular attribute
+                                               ///< reference.
+                                               ///< This forms a stack of tmpl_dcursor_nested_t
+                                               ///< and tracks where we are in evaluation at
+                                               ///< all levels.
+
+       tmpl_dcursor_nested_t   leaf;           //!< Pre-allocated leaf state.  We always need
+                                               ///< one of these so it doesn't make sense to
+                                               ///< allocate it later.
+};
+
+fr_pair_t              *tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc,
+                                          fr_dcursor_t *cursor, request_t *request,
+                                          tmpl_t const *vpt);
+
+void                   tmpl_dursor_clear(tmpl_dcursor_ctx_t *cc);
index 9ed98fb9369311fad5b6a178e5eca7213331b712..223693d996b361a19ddf07db9754cacbed99ef72 100644 (file)
@@ -31,9 +31,11 @@ RCSID("$Id$")
 #include <freeradius-devel/server/exec.h>
 #include <freeradius-devel/server/exec_legacy.h>
 #include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 #include <freeradius-devel/util/dlist.h>
 #include <freeradius-devel/util/proto.h>
 #include <freeradius-devel/util/value.h>
+#include <freeradius-devel/util/edit.h>
 
 /** Resolve attribute #pair_list_t value to an attribute list.
  *
@@ -45,7 +47,7 @@ RCSID("$Id$")
  *     name couldn't be resolved.
  * @return a pointer to the HEAD of a list in the #request_t.
  *
- * @see tmpl_pair_cursor_init
+ * @see tmpl_dcursor_init
  */
 fr_pair_list_t *tmpl_list_head(request_t *request, tmpl_pair_list_t list)
 {
@@ -778,440 +780,6 @@ ssize_t _tmpl_to_atype(TALLOC_CTX *ctx, void *out,
        return from_cast.vb_length;
 }
 
-static inline CC_HINT(always_inline)
-void _tmpl_cursor_pool_init(tmpl_pair_cursor_ctx_t *cc)
-{
-       if (!cc->pool) MEM(cc->pool = talloc_pool(cc->ctx, sizeof(tmpl_cursor_nested_t) * 5));
-}
-
-/** Traverse a group attribute
- *
- * Here we just look for a particular group attribute in the context of its parent
- *
- * @param[in] list_head The head of the pair_list being evaluated.
- * @param[in] current  The pair to evaluate.
- * @param[in] ns       Tracks tree position between cursor calls.
- * @return
- *     - the next matching attribute
- *     - NULL if none found
- */
-static fr_pair_t *_tmpl_cursor_child_eval(UNUSED fr_dlist_head_t *list_head, UNUSED fr_pair_t *current, tmpl_cursor_nested_t *ns)
-{
-       fr_pair_t *vp;
-
-       for (vp = fr_dcursor_current(&ns->group.cursor);
-            vp;
-            vp = fr_dcursor_next(&ns->group.cursor)) {
-               if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) {
-                       fr_dcursor_next(&ns->group.cursor);     /* Advance to correct position for next call */
-                       return vp;
-               }
-       }
-
-       return NULL;
-}
-
-/** Initialise the evaluation context for traversing a group attribute
- *
- */
-static inline CC_HINT(always_inline)
-void _tmpl_cursor_child_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_pair_cursor_ctx_t *cc)
-{
-       tmpl_cursor_nested_t *ns;
-
-       _tmpl_cursor_pool_init(cc);
-       MEM(ns = talloc(cc->pool, tmpl_cursor_nested_t));
-       *ns = (tmpl_cursor_nested_t){
-               .ar = ar,
-               .func = _tmpl_cursor_child_eval,
-               .list_ctx = list_ctx
-       };
-       fr_pair_dcursor_init(&ns->group.cursor, list);
-       fr_dlist_insert_tail(&cc->nested, ns);
-}
-
-/** Find a leaf attribute
- *
- * @param[in] list_head The head of the pair_list being evaluated.
- * @param[in] curr     The current attribute to start searching from.
- * @param[in] ns       Tracks tree position between cursor calls.
- * @return
- *     - the next matching attribute
- *     - NULL if none found
- */
-static fr_pair_t *_tmpl_cursor_leaf_eval(fr_dlist_head_t *list_head, fr_pair_t *curr, tmpl_cursor_nested_t *ns)
-{
-       fr_pair_t *vp = curr;
-
-       while (vp) {
-               if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) return vp;
-               vp = fr_dlist_next(list_head, vp);
-       }
-
-       return NULL;
-}
-
-/** Initialise the evaluation context for finding a leaf attribute
- *
- */
-static inline CC_HINT(always_inline)
-void _tmpl_cursor_leaf_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_pair_cursor_ctx_t *cc)
-{
-       tmpl_cursor_nested_t    *ns = &cc->leaf;
-
-       *ns = (tmpl_cursor_nested_t){
-               .ar = ar,
-               .func = _tmpl_cursor_leaf_eval,
-               .list_ctx = list_ctx
-       };
-       ns->leaf.list_head = list;
-       fr_dlist_insert_tail(&cc->nested, ns);
-}
-
-/** Stub list eval function until we can remove lists
- *
- */
-static fr_pair_t *_tmpl_cursor_list_eval(UNUSED fr_dlist_head_t *list_head, fr_pair_t *curr, UNUSED tmpl_cursor_nested_t *ns)
-{
-       return curr;
-}
-
-static inline CC_HINT(always_inline)
-void _tmpl_cursor_list_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_pair_cursor_ctx_t *cc)
-{
-       tmpl_cursor_nested_t *ns;
-
-       ns = &cc->leaf;
-       *ns = (tmpl_cursor_nested_t){
-               .ar = ar,
-               .func = _tmpl_cursor_list_eval,
-               .list_ctx = list_ctx
-       };
-       ns->leaf.list_head = list;
-       fr_dlist_insert_tail(&cc->nested, ns);
-}
-
-static inline CC_HINT(always_inline) void _tmpl_cursor_eval_pop(tmpl_pair_cursor_ctx_t *cc)
-{
-       tmpl_cursor_nested_t *ns = fr_dlist_pop_tail(&cc->nested);
-
-       if (ns != &cc->leaf) talloc_free(ns);
-}
-
-/** Evaluates, then, sometimes, pops evaluation contexts from the tmpl stack
- *
- * To pop or not to pop is determined by whether evaluating the context again
- * would/should/could produce another fr_pair_t.
- *
- * @param[in] list_head The head of the pair_list being evaluated.
- * @param[in] curr     The pair to evaluate.
- * @param[in] cc       Tracks state between cursor calls.
- * @return the vp evaluated.
- */
-static inline CC_HINT(always_inline)
-fr_pair_t *_tmpl_cursor_eval(fr_dlist_head_t *list_head, fr_pair_t *curr, tmpl_pair_cursor_ctx_t *cc)
-{
-       tmpl_attr_t const       *ar;
-       tmpl_cursor_nested_t    *ns;
-       fr_pair_t               *iter = curr, *vp;
-
-       ns = fr_dlist_tail(&cc->nested);
-       ar = ns->ar;
-
-       if (ar) switch (ar->ar_num) {
-       /*
-        *      Get the first instance
-        */
-       case NUM_ANY:
-               vp = ns->func(list_head, curr, ns);
-               _tmpl_cursor_eval_pop(cc);
-               break;
-
-       /*
-        *      Get all instances
-        */
-       case NUM_ALL:
-       case NUM_COUNT:
-       all_inst:
-               vp = ns->func(list_head, curr, ns);
-               if (!vp) _tmpl_cursor_eval_pop(cc);     /* pop only when we're done */
-               break;
-
-       /*
-        *      Get the last instance
-        */
-       case NUM_LAST:
-               vp = NULL;
-               while ((iter = ns->func(list_head, iter, ns))) {
-                       vp = iter;
-
-                       if (!fr_dlist_next(list_head, vp)) break;
-
-                       iter = fr_dlist_next(list_head,vp);
-               }
-               _tmpl_cursor_eval_pop(cc);
-               break;
-
-       /*
-        *      Get the n'th instance
-        */
-       default:
-       {
-               int16_t         i = 0;
-
-               for (;;) {
-                       vp = ns->func(list_head, iter, ns);
-                       if (!vp) break; /* Prev and next at the correct points */
-
-                       if (++i > ar->num) break;
-
-                       iter = fr_dlist_next(list_head, vp);
-               };
-               _tmpl_cursor_eval_pop(cc);
-       }
-               break;
-       } else goto all_inst;   /* Used for TMPL_TYPE_LIST */
-
-       return vp;
-}
-
-static inline CC_HINT(always_inline)
-void _tmpl_pair_cursor_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_pair_cursor_ctx_t *cc)
-{
-       if (fr_dlist_next(&cc->vpt->data.attribute.ar, ar)) switch (ar->ar_da->type) {
-       case FR_TYPE_STRUCTURAL:
-               _tmpl_cursor_child_init(list_ctx, list, ar, cc);
-               break;
-
-       default:
-       leaf:
-               _tmpl_cursor_leaf_init(list_ctx, list, ar, cc);
-               break;
-       } else goto leaf;
-}
-
-static void *_tmpl_cursor_next(fr_dlist_head_t *list, void *curr, void *uctx)
-{
-       tmpl_pair_cursor_ctx_t  *cc = uctx;
-       tmpl_t const            *vpt = cc->vpt;
-
-       fr_pair_t               *vp;
-       fr_pair_list_t          *list_head;
-
-       switch (vpt->type) {
-       case TMPL_TYPE_ATTR:
-       {
-               tmpl_attr_t const       *ar = NULL;
-               tmpl_cursor_nested_t    *ns = NULL;
-
-               /*
-                *      - Continue until there are no evaluation contexts
-                *      - Push a evaluation context if evaluating the head of the
-                *        stack yields a VP and we're not at the deepest attribute
-                *        reference.
-                *      - Return if we have a VP and there are no more attribute
-                *        references to push, i.e. we're at the deepest attribute
-                *        reference.
-                */
-               while ((ns = fr_dlist_tail(&cc->nested))) {
-                       ar = ns->ar;
-                       vp = _tmpl_cursor_eval(list, curr, cc);
-                       if (!vp) continue;
-
-                       ar = fr_dlist_next(&vpt->data.attribute.ar, ar);
-                       if (ar) {
-                               list_head = &vp->vp_group;
-                               _tmpl_pair_cursor_init(vp, list_head, ar, cc);
-                               curr = fr_pair_list_head(list_head);
-                               list = UNCONST(fr_dlist_head_t *, fr_pair_list_order(list_head));
-                               continue;
-                       }
-
-                       return vp;
-               }
-
-       null_result:
-               return NULL;
-       }
-
-       /*
-        *      Hacks for evaluating lists
-        *      Hopefully this tmpl type goes away soon...
-        */
-       case TMPL_TYPE_LIST:
-               if (!fr_dlist_tail(&cc->nested)) goto null_result;      /* end of list */
-
-               vp = _tmpl_cursor_eval(list, curr, cc);
-               if (!vp) goto null_result;
-
-               return vp;
-
-       default:
-               fr_assert(0);
-       }
-
-       return NULL;
-}
-
-/** Initialise a #fr_dcursor_t to the #fr_pair_t specified by a #tmpl_t
- *
- * This makes iterating over the one or more #fr_pair_t specified by a #tmpl_t
- * significantly easier.
- *
- * @param[out] err             May be NULL if no error code is required.
- *                             Will be set to:
- *                             - 0 on success.
- *                             - -1 if no matching #fr_pair_t could be found.
- *                             - -2 if list could not be found (doesn't exist in current #request_t).
- *                             - -3 if context could not be found (no parent #request_t available).
- * @param[in] ctx              to make temporary allocations under.
- * @param[in] cc               to initialise.  Tracks evaluation state.
- *                             Must be explicitly cleared with tmpl_cursor_state_clear
- *                             otherwise we will leak memory.
- * @param[in] cursor           to store iterator position.
- * @param[in] request          The current #request_t.
- * @param[in] vpt              specifying the #fr_pair_t type or list to iterate over.
- * @return
- *     - First #fr_pair_t specified by the #tmpl_t.
- *     - NULL if no matching #fr_pair_t found, and NULL on error.
- *
- * @see tmpl_cursor_next
- */
-fr_pair_t *tmpl_pair_cursor_init(int *err, TALLOC_CTX *ctx, tmpl_pair_cursor_ctx_t *cc,
-                                fr_dcursor_t *cursor, request_t *request, tmpl_t const *vpt)
-{
-       fr_pair_t               *vp = NULL;
-       fr_pair_list_t          *list_head = NULL;
-       tmpl_request_t          *rr = NULL;
-       TALLOC_CTX              *list_ctx = NULL;
-
-       TMPL_VERIFY(vpt);
-
-       fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
-
-       if (err) *err = 0;
-
-       /*
-        *      Navigate to the correct request context
-        */
-       while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
-               if (tmpl_request_ptr(&request, rr->request) < 0) {
-                       if (err) {
-                               *err = -3;
-                               fr_strerror_printf("Request context \"%s\" not available",
-                                                  fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
-                       }
-               error:
-                       memset(cc, 0, sizeof(*cc));     /* so tmpl_pair_cursor_clear doesn't explode */
-                       return NULL;
-               }
-       }
-
-       /*
-        *      If the rules say "return the list as an attribute",
-        *      then check for that.  But if the first da isn't a
-        *      list, then default to whatever list ref is given.
-        *
-        *      @todo - for the future, have the tokenizer (or
-        *      whatever) always add the list head to the list of
-        *      attribute references.
-        */
-       if (vpt->rules.list_as_attr) {
-               tmpl_attr_t const *ar;
-
-               ar = fr_dlist_head(&vpt->data.attribute.ar);
-               fr_assert(ar != NULL);
-
-               if ((ar->ar_da == request_attr_request) ||
-                   (ar->ar_da == request_attr_reply) ||
-                   (ar->ar_da == request_attr_control) ||
-                   (ar->ar_da == request_attr_state)) {
-                       list_head = &request->pair_root->vp_group;
-                       list_ctx = request->pair_root;
-               }
-       }
-
-       if (!list_head) {
-               /*
-                *      Get the right list in the specified context
-                */
-               list_head = tmpl_list_head(request, tmpl_list(vpt));
-               if (!list_head) {       
-                       if (err) {
-                               *err = -2;
-                               fr_strerror_printf("List \"%s\" not available in this context",
-                                                  fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
-                       }
-                       goto error;
-               }
-               list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
-       }
-       fr_assert(list_ctx != NULL);
-
-       /*
-        *      Initialise the temporary cursor context
-        */
-       *cc = (tmpl_pair_cursor_ctx_t){
-               .vpt = vpt,
-               .ctx = ctx,
-               .request = request,
-               .list = list_head
-       };
-       fr_dlist_init(&cc->nested, tmpl_cursor_nested_t, entry);
-
-       /*
-        *      Prime the stack!
-        */
-       switch (vpt->type) {
-       case TMPL_TYPE_ATTR:
-               _tmpl_pair_cursor_init(list_ctx, cc->list, fr_dlist_head(&vpt->data.attribute.ar), cc);
-               break;
-
-       case TMPL_TYPE_LIST:
-               _tmpl_cursor_list_init(list_ctx, cc->list, fr_dlist_head(&vpt->data.attribute.ar), cc);
-               break;
-
-       default:
-               fr_assert(0);
-               break;
-       }
-
-       /*
-        *      Get the first entry from the tmpl
-        */
-       vp = fr_pair_dcursor_iter_init(cursor, list_head, _tmpl_cursor_next, cc);
-       if (!vp) {
-               if (err) {
-                       *err = -1;
-                       if (tmpl_is_list(vpt)) {
-                               fr_strerror_printf("List \"%s\" is empty", vpt->name);
-                       } else {
-                               fr_strerror_printf("No matching \"%s\" pairs found", tmpl_da(vpt)->name);
-                       }
-               }
-               return NULL;
-       }
-
-       return vp;
-}
-
-/** Clear any temporary state allocations
- *
- */
-void tmpl_pair_cursor_clear(tmpl_pair_cursor_ctx_t *cc)
-{
-       if (!fr_dlist_num_elements(&cc->nested)) return;/* Help simplify dealing with unused cursor ctxs */
-
-       fr_dlist_remove(&cc->nested, &cc->leaf);        /* Noop if leaf isn't inserted */
-       fr_dlist_talloc_free(&cc->nested);
-
-       /*
-        *      Always free the pool because it's allocated when
-        *      any nested ctxs are used.
-        */
-       TALLOC_FREE(cc->pool);
-}
-
 /** Copy pairs matching a #tmpl_t in the current #request_t
  *
  * @param ctx to allocate new #fr_pair_t in.
@@ -1231,7 +799,7 @@ int tmpl_copy_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, tm
 {
        fr_pair_t               *vp;
        fr_dcursor_t            from;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
 
        TMPL_VERIFY(vpt);
 
@@ -1239,7 +807,7 @@ int tmpl_copy_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, tm
 
        fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
 
-       for (vp = tmpl_pair_cursor_init(&err, NULL, &cc, &from, request, vpt);
+       for (vp = tmpl_dcursor_init(&err, NULL, &cc, &from, request, vpt);
             vp;
             vp = fr_dcursor_next(&from)) {
                vp = fr_pair_copy(ctx, vp);
@@ -1251,7 +819,7 @@ int tmpl_copy_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, tm
                }
                fr_pair_append(out, vp);
        }
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
 
        return err;
 }
@@ -1276,7 +844,7 @@ int tmpl_copy_pair_children(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *req
 {
        fr_pair_t               *vp;
        fr_dcursor_t            from;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
 
        TMPL_VERIFY(vpt);
 
@@ -1286,7 +854,7 @@ int tmpl_copy_pair_children(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *req
 
        fr_pair_list_free(out);
 
-       for (vp = tmpl_pair_cursor_init(&err, NULL, &cc, &from, request, vpt);
+       for (vp = tmpl_dcursor_init(&err, NULL, &cc, &from, request, vpt);
             vp;
             vp = fr_dcursor_next(&from)) {
                switch (vp->da->type) {
@@ -1302,7 +870,7 @@ int tmpl_copy_pair_children(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *req
                }
        }
 done:
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
 
        return err;
 }
@@ -1325,14 +893,14 @@ done:
 int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
 {
        fr_dcursor_t            cursor;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
        fr_pair_t               *vp;
        int                     err;
 
        TMPL_VERIFY(vpt);
 
-       vp = tmpl_pair_cursor_init(&err, request, &cc, &cursor, request, vpt);
-       tmpl_pair_cursor_clear(&cc);
+       vp = tmpl_dcursor_init(&err, request, &cc, &cursor, request, vpt);
+       tmpl_dursor_clear(&cc);
 
        if (out) *out = vp;
 
@@ -1354,7 +922,7 @@ int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
 int tmpl_find_or_add_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
 {
        fr_dcursor_t            cursor;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
        fr_pair_t               *vp;
        int                     err;
 
@@ -1363,8 +931,8 @@ int tmpl_find_or_add_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
 
        *out = NULL;
 
-       vp = tmpl_pair_cursor_init(&err, NULL, &cc, &cursor, request, vpt);
-       tmpl_pair_cursor_clear(&cc);
+       vp = tmpl_dcursor_init(&err, NULL, &cc, &cursor, request, vpt);
+       tmpl_dursor_clear(&cc);
 
        switch (err) {
        case 0:
@@ -1391,287 +959,3 @@ int tmpl_find_or_add_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
                return err;
        }
 }
-
-/** Determines points where the reference list extends beyond the current pair tree
- *
- * If a particular branch in the VP hierarchy is incomplete, i.e. the chain of attribute
- * refers to nodes deeper than the nodes currently in the tree, then we return the
- * deepest point node in the tree which matched, and the ar that we failed to evaluate.
- *
- * If the reference list resolves to one or more structural pairs, return those as well.
- *
- * This function can be used for a number of different operations, but it's most useful
- * for determining insertion points for new attributes, or determining which attributes
- * need to be updated.
- *
- * @param[in] ctx              to allocate.  It's recommended to pass a pool with space
- *                             for at least five extent structures.
- * @param[out] leaf            List of extents we discovered by evaluating all
- *                             attribute references. May be NULL.
- * @param[out] interior        List of extents that need building out, i.e. references
- *                             extend beyond pairs. May be NULL.
- * @param[in] request          The current #request_t.
- * @param[in] vpt              specifying the #fr_pair_t type to retrieve or create.
- *                             Must be #TMPL_TYPE_ATTR.
- * @return
- *     - 1 on success a pair was created.
- *     - 0 on success a pair was found.
- *     - -1 if a new #fr_pair_t couldn't be found or created.
- *     - -2 if list could not be found (doesn't exist in current #request_t).
- *     - -3 if context could not be found (no parent #request_t available).
- */
-int tmpl_extents_find(TALLOC_CTX *ctx,
-                     fr_dlist_head_t *leaf, fr_dlist_head_t *interior,
-                     request_t *request, tmpl_t const *vpt)
-{
-       fr_pair_t               *curr = NULL;
-       fr_pair_list_t          *list_head;
-
-       TALLOC_CTX              *list_ctx = NULL;
-
-       tmpl_pair_cursor_ctx_t  cc;
-       tmpl_cursor_nested_t    *ns = NULL;
-
-       tmpl_request_t          *rr = NULL;
-       tmpl_attr_t const       *ar = NULL;
-
-       TMPL_VERIFY(vpt);
-
-       fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
-
-#define EXTENT_ADD(_out, _ar, _list_ctx, _list) \
-       do { \
-               tmpl_attr_extent_t      *_extent; \
-               MEM(_extent = talloc(ctx, tmpl_attr_extent_t)); \
-               *_extent = (tmpl_attr_extent_t){ \
-                       .ar = _ar,      \
-                       .list_ctx = _list_ctx, \
-                       .list = _list   \
-               }; \
-               fr_dlist_insert_tail(_out, _extent); \
-       } while (0)
-
-       /*
-        *      Navigate to the correct request context
-        */
-       while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
-               if (tmpl_request_ptr(&request, rr->request) < 0) {
-                       fr_strerror_printf("Request context \"%s\" not available",
-                                          fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
-                       return -3;
-               }
-       }
-
-       /*
-        *      Get the right list in the specified context
-        */
-       list_head = tmpl_list_head(request, tmpl_list(vpt));
-       if (!list_head) {
-               fr_strerror_printf("List \"%s\" not available in this context",
-                                  fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
-               return -2;
-       }
-       list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
-
-       /*
-        *      If it's a list, just return the list head
-        */
-       if (vpt->type == TMPL_TYPE_LIST) {
-       do_list:
-               if (leaf) EXTENT_ADD(leaf, NULL, list_ctx, list_head);
-               return 0;
-       }
-
-       /*
-        *      If it's a leaf skip all the expensive
-        *      initialisation and just return the list
-        *      it's part of.
-        *
-        *      This is only needed because lists are
-        *      treated specially.  Once lists are groups
-        *      this can be removed.
-        */
-       ar = fr_dlist_head(&vpt->data.attribute.ar);
-       switch (ar->ar_da->type) {
-       case FR_TYPE_STRUCTURAL:
-               break;
-
-       default:
-               goto do_list;
-       }
-
-       /*
-        *      Initialise the temporary cursor context
-        */
-       cc = (tmpl_pair_cursor_ctx_t){
-               .vpt = vpt,
-               .ctx = ctx,
-               .request = request,
-               .list = list_head
-       };
-       fr_dlist_init(&cc.nested, tmpl_cursor_nested_t, entry);
-
-       /*
-        *      Prime the stack!
-        */
-       _tmpl_pair_cursor_init(list_ctx, cc.list, fr_dlist_head(&vpt->data.attribute.ar), &cc);
-
-       /*
-        *      - Continue until there are no evaluation contexts
-        *      - Push a evaluation context if evaluating the head of the
-        *        stack yields a VP and we're not at the deepest attribute
-        *        reference.
-        *      - Return if we have a VP and there are no more attribute
-        *        references to push, i.e. we're at the deepest attribute
-        *        reference.
-        */
-       curr = fr_pair_list_head(list_head);
-       while ((ns = fr_dlist_tail(&cc.nested))) {
-               tmpl_attr_t const *n_ar;
-
-               list_ctx = ns->list_ctx;
-               ar = ns->ar;
-               curr = _tmpl_cursor_eval(&list_head->order, curr, &cc);
-               if (!curr) {
-                       /*
-                        *      References extend beyond current
-                        *      pair tree.
-                        */
-                       if (!ar->resolve_only && interior) EXTENT_ADD(interior, ar, list_ctx, list_head);
-                       continue;       /* Rely on _tmpl_cursor_eval popping the stack */
-               }
-
-               /*
-                *      Evaluate the next reference
-                */
-               n_ar = fr_dlist_next(&vpt->data.attribute.ar, ar);
-               if (n_ar) {
-                       ar = n_ar;
-                       list_head = &curr->vp_group;
-                       list_ctx = curr;        /* Allocations are under the group */
-                       _tmpl_pair_cursor_init(list_ctx, list_head, ar, &cc);
-                       curr = fr_pair_list_head(list_head);
-                       continue;
-               }
-
-               /*
-                *      VP tree may extend beyond
-                *      the reference. If the reference
-                *      was structural, record this as
-                *      an extent.
-                */
-               switch (ar->da->type) {
-               case FR_TYPE_STRUCTURAL:
-                       if (leaf) EXTENT_ADD(leaf, NULL, curr, list_head);
-                       continue;
-
-               default:
-                       break;
-               }
-       }
-
-       return 0;
-}
-
-/** Allocate interior pairs
- *
- * Builds out the pair tree to the point where leaf attributes can be added
- *
- * @param[out] leaf    List to add built out attributes to.
- * @param[in] interior List to remove attributes from.
- * @param[in] vpt      We are evaluating.
- * @return
- *     - 0 on success.
- *      - -1 on failure.
- */
-int tmpl_extents_build_to_leaf(fr_dlist_head_t *leaf, fr_dlist_head_t *interior, tmpl_t const *vpt)
-{
-       tmpl_attr_extent_t      *extent = NULL;
-
-       while ((extent = fr_dlist_head(interior))) {
-               fr_pair_t               *vp;
-               fr_pair_list_t          *list;
-               tmpl_attr_t const       *ar;
-               TALLOC_CTX              *list_ctx = extent->list_ctx;
-
-               fr_assert(extent->ar);  /* Interior extents MUST contain an ar */
-
-               /*
-                *      Try and allocate VPs for the
-                *      rest of the attribute references.
-                */
-               for (ar = extent->ar, list = extent->list;
-                    ar;
-                    ar = fr_dlist_next(&vpt->data.attribute.ar, ar)) {
-                       switch (ar->type) {
-                       case TMPL_ATTR_TYPE_NORMAL:
-                       case TMPL_ATTR_TYPE_UNKNOWN:
-                               /*
-                                *      Don't build leaf attributes
-                                */
-                               switch (ar->ar_da->type) {
-                               case FR_TYPE_STRUCTURAL:
-                                       break;
-
-                               default:
-                                       continue;
-                               }
-
-                               MEM(vp = fr_pair_afrom_da(list_ctx, ar->ar_da));        /* Copies unknowns */
-                               fr_pair_append(list, vp);
-                               list = &vp->vp_group;
-                               list_ctx = vp;          /* New allocations occur under the VP */
-                               break;
-
-                       default:
-                               fr_assert_fail("references of this type should have been resolved");
-                               return -1;
-                       }
-               }
-
-               fr_dlist_remove(interior, extent);      /* Do this *before* zeroing the dlist headers */
-               *extent = (tmpl_attr_extent_t){
-                       .list = list,
-                       .list_ctx = list_ctx
-               };
-               fr_dlist_insert_tail(leaf, extent);     /* move between in and out */
-       }
-
-       return 0;
-}
-
-void tmpl_extents_debug(fr_dlist_head_t *head)
-{
-       tmpl_attr_extent_t const *extent = NULL;
-       fr_pair_t *vp = NULL;
-
-       for (extent = fr_dlist_head(head);
-            extent;
-            extent = fr_dlist_next(head, extent)) {
-               tmpl_attr_t const *ar = extent->ar;
-               char const *ctx_name;
-
-               if (ar) {
-                       FR_FAULT_LOG("extent-interior-attr");
-                       tmpl_attr_ref_debug(extent->ar, 0);
-               } else {
-                       FR_FAULT_LOG("extent-leaf");
-               }
-
-               ctx_name = talloc_get_name(extent->list_ctx);
-               if (strcmp(ctx_name, "fr_pair_t") == 0) {
-                       FR_FAULT_LOG("list_ctx     : %p (%s, %s)", extent->list_ctx, ctx_name,
-                                    ((fr_pair_t *)extent->list_ctx)->da->name);
-               } else {
-                       FR_FAULT_LOG("list_ctx     : %p (%s)", extent->list_ctx, ctx_name);
-               }
-               FR_FAULT_LOG("list         : %p", extent->list);
-               if (fr_pair_list_empty(extent->list)) {
-                       FR_FAULT_LOG("list (first) : none (%p)", extent->list);
-               } else {
-                       vp = fr_pair_list_head(extent->list);
-                       FR_FAULT_LOG("list (first) : %s (%p)", vp->da->name, extent->list);
-               }
-       }
-
-}
index 5076642bdc759723110acf4cca70e4f3dec146be..87c42131697abd11fbdc0c6135a606e6b1062b65 100644 (file)
@@ -101,7 +101,7 @@ static unlang_action_t unlang_subrequest_parent_resume(rlm_rcode_t *p_result, re
                 *      Find out what we need to build and build it
                 */
                if ((tmpl_extents_find(state, &leaf, &interior, request, gext->dst) < 0) ||
-                   (tmpl_extents_build_to_leaf(&leaf, &interior, gext->dst) < 0)) {
+                   (tmpl_extents_build_to_leaf_parent(&leaf, &interior, gext->dst) < 0)) {
                        RPDEBUG("Discarding subrequest attributes - Failed allocating groups");
                        fr_dlist_talloc_free(&leaf);
                        fr_dlist_talloc_free(&interior);
index cbe7579933997613bf3cb62ee6edf137788957f2..d5ab2e360682feaaf0db9490415d56e74b8a3774 100644 (file)
@@ -31,6 +31,7 @@ RCSID("$Id$")
  */
 
 #include <freeradius-devel/server/base.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 #include <freeradius-devel/unlang/xlat_priv.h>
 
 #include <freeradius-devel/io/test_point.h>
@@ -999,7 +1000,7 @@ static xlat_action_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcur
 {
        fr_pair_t               *vp;
        fr_dcursor_t            cursor;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
        tmpl_t                  *vpt;
        fr_value_box_t          *attr = fr_dlist_head(in);
        char const              *fmt;
@@ -1020,7 +1021,7 @@ static xlat_action_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcur
        RIDEBUG("Attributes matching \"%s\"", fmt);
 
        RINDENT();
-       for (vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+       for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
             vp;
             vp = fr_dcursor_next(&cursor)) {
                fr_dict_vendor_t const          *vendor;
@@ -1105,7 +1106,7 @@ static xlat_action_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcur
                        talloc_free(dst);
                }
        }
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        REXDENT();
 
        talloc_free(vpt);
@@ -2337,7 +2338,7 @@ static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_dcursor_t *out,
 {
        tmpl_t                  *vpt = NULL;
        fr_dcursor_t            cursor;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
        fr_value_box_t          *vb;
        fr_value_box_t          *in_head = fr_dlist_head(in);
 
@@ -2352,7 +2353,7 @@ static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_dcursor_t *out,
                return XLAT_ACTION_FAIL;
        }
 
-       for (vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+       for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
             vp;
             vp = fr_dcursor_next(&cursor)) {
                fr_token_t op = vp->op;
@@ -2367,7 +2368,7 @@ static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_dcursor_t *out,
                fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, false);
                fr_dcursor_append(out, vb);
        }
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        talloc_free(vpt);
 
        return XLAT_ACTION_DONE;
@@ -3384,7 +3385,7 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        tmpl_t          *vpt;
        fr_pair_t       *vp;
        fr_dcursor_t    cursor;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
        bool            tainted = false;
        fr_value_box_t  *encoded;
 
@@ -3420,7 +3421,7 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        /*
         *      Loop over the attributes, encoding them.
         */
-       for (vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+       for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
             vp != NULL;
             vp = fr_dcursor_next(&cursor)) {
                if (vp->da->flags.internal) continue;
@@ -3440,7 +3441,7 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
                len = tp_encode->func(&FR_DBUFF_TMP(p, end), &cursor, encode_ctx);
                if (len < 0) {
                        RPEDEBUG("Protocol encoding failed");
-                       tmpl_pair_cursor_clear(&cc);
+                       tmpl_dursor_clear(&cc);
                        talloc_free(vpt);
                        return XLAT_ACTION_FAIL;
                }
@@ -3449,7 +3450,7 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
                p += len;
        }
 
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        talloc_free(vpt);
 
        /*
index eeec8b002d140da9bac2646b75ca30f3519be00a..19015f572edce799e6e09c61e462a4bb459c3618 100644 (file)
@@ -28,8 +28,9 @@
 RCSID("$Id$")
 
 #include <freeradius-devel/server/base.h>
-#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 #include <freeradius-devel/unlang/xlat_priv.h>
+#include <freeradius-devel/util/debug.h>
 
 #include <freeradius-devel/unlang/unlang_priv.h>       /* Remove when everything uses new xlat API */
 
@@ -819,7 +820,7 @@ xlat_eval_pair_real(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *reques
        fr_value_box_t          *value;
 
        fr_dcursor_t            cursor;
-       tmpl_pair_cursor_ctx_t  cc;
+       tmpl_dcursor_ctx_t      cc;
 
        xlat_action_t           ret = XLAT_ACTION_DONE;
 
@@ -831,7 +832,7 @@ xlat_eval_pair_real(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *reques
         *      This allows users to manipulate virtual attributes as if
         *      they were real ones.
         */
-       vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+       vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
 
        /*
         *      We didn't find the VP in a list, check to see if it's
@@ -902,7 +903,7 @@ xlat_eval_pair_real(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *reques
        default:
                /*
                 *      The cursor was set to the correct
-                *      position above by tmpl_pair_cursor_init.
+                *      position above by tmpl_dcursor_init.
                 */
                vp = fr_dcursor_current(&cursor);                       /* NULLness checked above */
                value = fr_value_box_alloc(ctx, vp->data.type, vp->da, vp->data.tainted);
@@ -914,7 +915,7 @@ xlat_eval_pair_real(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *reques
        }
 
 done:
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        return ret;
 }
 
index f7e67198ae15136a264e2139960c7ac6293ed135..28ce81ed0a233cf15907b3bec32352cb55fb1c4a 100644 (file)
@@ -596,7 +596,7 @@ fr_edit_list_t *fr_edit_list_alloc(TALLOC_CTX *ctx, int hint)
  *  inserting pairs in a list, then we find the lowest existing pair,
  *  and add pairs there.
  *
- *  The functions tmpl_extents_find() and tmpl_extents_build_to_leaf()
+ *  The functions tmpl_extents_find() and tmpl_extents_build_to_leaf_parent()
  *  should help us figure out where the VPs exist or not.
  *
  *  The overall "update" algorithm is now:
index c23c9b292a04cbead9e6e53cd5ff391c41febf4f..974ae8da99a553dbeb467f831eac6c3325d1b56f 100644 (file)
@@ -26,8 +26,8 @@ RCSID("$Id$")
 USES_APPLE_DEPRECATED_API
 
 #include <freeradius-devel/server/base.h>
-
 #include <freeradius-devel/server/module.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 #include <freeradius-devel/util/debug.h>
 
 #include <ctype.h>
@@ -212,7 +212,7 @@ static bool get_number(request_t *request, char const **string, int64_t *answer)
                ssize_t                 slen;
                fr_pair_t               *vp;
                fr_dcursor_t            cursor;
-               tmpl_pair_cursor_ctx_t  cc;
+               tmpl_dcursor_ctx_t      cc;
 
                slen = tmpl_afrom_attr_substr(request, NULL, &vpt,
                                              &FR_SBUFF_IN(p, strlen(p)),
@@ -237,7 +237,7 @@ static bool get_number(request_t *request, char const **string, int64_t *answer)
                }
 
                x = 0;
-               for (i = 0, vp = tmpl_pair_cursor_init(&err, NULL, &cc, &cursor, request, vpt);
+               for (i = 0, vp = tmpl_dcursor_init(&err, NULL, &cc, &cursor, request, vpt);
                     (i < max) && (vp != NULL);
                     i++, vp = fr_dcursor_next(&cursor)) {
                        int64_t         y;
@@ -291,7 +291,7 @@ static bool get_number(request_t *request, char const **string, int64_t *answer)
                                        RPEDEBUG("Failed converting &%.*s to an integer value", (int) vpt->len,
                                                 vpt->name);
                                error:
-                                       tmpl_pair_cursor_clear(&cc);
+                                       tmpl_dursor_clear(&cc);
                                        return false;
                                }
                                if (value.vb_uint64 > INT64_MAX) {
@@ -318,7 +318,7 @@ static bool get_number(request_t *request, char const **string, int64_t *answer)
 
                        x += y;
                } /* loop over all found VPs */
-               tmpl_pair_cursor_clear(&cc);
+               tmpl_dursor_clear(&cc);
 
                if (err != 0) {
                        RWDEBUG("Can't find &%.*s.  Using 0 as operand value", (int)vpt->len, vpt->name);
index 9c480dc606239e26f3dd691a10b3d7d017fc5f1a..474a63f1bcf559c6f5cbf70f8eea961e90eeffef 100644 (file)
@@ -26,6 +26,7 @@ RCSID("$Id$")
 #include <freeradius-devel/server/base.h>
 #include <freeradius-devel/server/exfile.h>
 #include <freeradius-devel/server/module.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/perm.h>
 
@@ -561,12 +562,12 @@ build_vector:
        {
                #define VECTOR_INCREMENT 20
                fr_dcursor_t            cursor;
-               tmpl_pair_cursor_ctx_t  cc;
+               tmpl_dcursor_ctx_t      cc;
                fr_pair_t               *vp;
                int                     alloced = VECTOR_INCREMENT, i;
 
                MEM(vector = talloc_array(request, struct iovec, alloced));
-               for (vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt_p), i = 0;
+               for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt_p), i = 0;
                     vp;
                     vp = fr_dcursor_next(&cursor), i++) {
                        /* need extra for line terminator */
@@ -598,7 +599,7 @@ build_vector:
                                vector[i].iov_len = inst->delimiter_len;
                        }
                }
-               tmpl_pair_cursor_clear(&cc);
+               tmpl_dursor_clear(&cc);
                vector_p = vector;
                vector_len = i;
        }
index 6dfea892f27a4f522b4cd683ee33549d4e78b181..b31900132f97a2e287a725d74439256a8f374311 100644 (file)
  */
 RCSID("$Id$")
 
+#include <freeradius-devel/curl/base.h>
 #include <freeradius-devel/server/base.h>
+#include <freeradius-devel/server/cf_priv.h>
 #include <freeradius-devel/server/module.h>
-#include <freeradius-devel/curl/base.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 #include <freeradius-devel/util/talloc.h>
-#include <freeradius-devel/server/cf_priv.h>
 
 static fr_dict_t const         *dict_radius; /*dictionary for radius protocol*/
 static fr_dict_t const         *dict_freeradius;
@@ -219,18 +220,18 @@ static int tmpl_attr_to_slist(fr_mail_ctx_t *uctx, struct curl_slist **out, tmpl
 {
        request_t                       *request = ((fr_mail_ctx_t *)uctx)->request;
        fr_pair_t                       *vp;
-       tmpl_pair_cursor_ctx_t          cc;
+       tmpl_dcursor_ctx_t              cc;
        int                             count = 0;
 
        /* Iterate over the VP and add the string value to the curl_slist */
-       vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &uctx->cursor, request, tmpl);
+       vp = tmpl_dcursor_init(NULL, NULL, &cc, &uctx->cursor, request, tmpl);
        while (vp) {
                count += 1;
                *out = curl_slist_append(*out, vp->vp_strvalue);
                vp = fr_dcursor_next(&uctx->cursor);
        }
        /* Return the number of elements that were found */
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        return count;
 }
 
@@ -267,12 +268,12 @@ static int tmpl_arr_to_slist (rlm_smtp_thread_t *t, fr_mail_ctx_t *uctx, struct
 static ssize_t tmpl_attr_to_sbuff (fr_mail_ctx_t *uctx, fr_sbuff_t *out, tmpl_t const *vpt, char const *delimeter)
 {
        fr_pair_t               *vp;
-       tmpl_pair_cursor_ctx_t       cc;
+       tmpl_dcursor_ctx_t       cc;
 
        ssize_t                 copied = 0;
 
        /* Loop through the elements to be added to the sbuff */
-       vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &uctx->cursor, uctx->request, vpt);
+       vp = tmpl_dcursor_init(NULL, NULL, &cc, &uctx->cursor, uctx->request, vpt);
        while (vp) {
                copied += fr_sbuff_in_bstrncpy(out, vp->vp_strvalue, vp->vp_length);
                vp = fr_dcursor_next(&uctx->cursor);
@@ -281,7 +282,7 @@ static ssize_t tmpl_attr_to_sbuff (fr_mail_ctx_t *uctx, fr_sbuff_t *out, tmpl_t
                        copied += fr_sbuff_in_strcpy(out, delimeter);
                }
        }
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        return copied;
 }
 
@@ -377,11 +378,11 @@ static int tmpl_attr_to_attachment (fr_mail_ctx_t *uctx, curl_mime *mime, const
 {
        fr_pair_t               *vp;
        request_t                       *request = uctx->request;
-       tmpl_pair_cursor_ctx_t       cc;
+       tmpl_dcursor_ctx_t       cc;
        int                     attachments_set = 0;
 
        /* Check for any file attachments */
-       for( vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &uctx->cursor, request, tmpl);
+       for( vp = tmpl_dcursor_init(NULL, NULL, &cc, &uctx->cursor, request, tmpl);
        vp;
                vp = fr_dcursor_next(&uctx->cursor)){
                if(vp->vp_tainted) {
@@ -390,7 +391,7 @@ static int tmpl_attr_to_attachment (fr_mail_ctx_t *uctx, curl_mime *mime, const
                }
                attachments_set += str_to_attachments(uctx, mime, vp->vp_strvalue, vp->vp_length, path_buffer, m);
        }
-       tmpl_pair_cursor_clear(&cc);
+       tmpl_dursor_clear(&cc);
        return attachments_set;
 }