tree data UPDATE support for milti-target leafref links (#2161)

* Adding support for multiple leafref target nodes
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 6b2fa39..6fd4228 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -988,7 +988,7 @@
 
 LIBYANG_API_DEF LY_ERR
 lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct lyd_node *node, struct lyd_value *value,
-        const struct lyd_node *tree, struct lyd_node **target, char **errmsg)
+        const struct lyd_node *tree, struct ly_set **targets, char **errmsg)
 {
     LY_ERR rc = LY_SUCCESS;
     struct lyxp_expr *target_path = NULL;
@@ -999,10 +999,10 @@
 
     LY_CHECK_ARG_RET(NULL, lref, node, value, errmsg, LY_EINVAL);
 
-    if (target) {
-        *target = NULL;
-    }
     *errmsg = NULL;
+    if (targets) {
+        *targets = NULL;
+    }
 
     /* get the canonical value */
     val_str = lyd_value_get_canonical(LYD_CTX(node), value);
@@ -1070,8 +1070,21 @@
         }
         goto cleanup;
     }
-    if (target) {
-        *target = set.val.nodes[i].node;
+    if (targets) {
+        LY_CHECK_GOTO(rc = ly_set_new(targets), cleanup);
+        for (i = 0; i < set.used; ++i) {
+            if (set.val.nodes[i].type != LYXP_NODE_ELEM) {
+                continue;
+            }
+            if (((struct lyd_node_term *)set.val.nodes[i].node)->value.realtype != value->realtype) {
+                continue;
+            }
+
+            if (!lref->plugin->compare(&((struct lyd_node_term *)set.val.nodes[i].node)->value, value)) {
+                rc = ly_set_add(*targets, set.val.nodes[i].node, 0, NULL);
+                LY_CHECK_GOTO(rc, cleanup);
+            }
+        }
     }
 
 cleanup:
diff --git a/src/plugins_types.h b/src/plugins_types.h
index b4ecd43..e3b9545 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -21,6 +21,7 @@
 #include "config.h"
 #include "log.h"
 #include "plugins.h"
+#include "set.h"
 #include "tree.h"
 
 #include "tree_edit.h"
@@ -1199,12 +1200,12 @@
  * @param[in] node Context node.
  * @param[in] value Target value.
  * @param[in] tree Full data tree to search in.
- * @param[out] target Optional found target.
+ * @param[out] target Pointer to set of target nodes, optional.
  * @param[out] errmsg Error message in case of error.
  * @return LY_ERR value.
  */
 LIBYANG_API_DECL LY_ERR lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct lyd_node *node,
-        struct lyd_value *value, const struct lyd_node *tree, struct lyd_node **target, char **errmsg);
+        struct lyd_value *value, const struct lyd_node *tree, struct ly_set **targets, char **errmsg);
 
 /** @} pluginsTypes */
 
diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c
index 8e60f98..f4e24ca 100644
--- a/src/plugins_types/leafref.c
+++ b/src/plugins_types/leafref.c
@@ -70,7 +70,8 @@
     LY_ERR ret;
     struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
     char *errmsg = NULL, *path;
-    struct lyd_node *target = NULL;
+    struct ly_set *targets = NULL;
+    uint32_t i;
 
     *err = NULL;
 
@@ -79,19 +80,24 @@
         return LY_SUCCESS;
     }
 
-    /* check leafref target existence */
-    if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, &target, &errmsg)) {
+    ret = lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, (ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_LINKING) ? &targets : NULL, &errmsg);
+    if (ret != LY_SUCCESS) {
         path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
         ret = ly_err_new(err, LY_EVALID, LYVE_DATA, path, strdup("instance-required"), "%s", errmsg);
         free(errmsg);
-        return ret;
+        goto cleanup;
     }
 
     if (ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_LINKING) {
-        return lyd_link_leafref_node((struct lyd_node_term *)target, (struct lyd_node_term *)ctx_node);
+        for (i = 0; i < targets->count; ++i) {
+            ret = lyd_link_leafref_node((struct lyd_node_term *)targets->dnodes[i], (struct lyd_node_term *)ctx_node);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
     }
 
-    return LY_SUCCESS;
+cleanup:
+    ly_set_free(targets, NULL);
+    return ret;
 }
 
 LIBYANG_API_DEF LY_ERR
diff --git a/src/tree_data.c b/src/tree_data.c
index ebe7eaf..8a6de8a 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -3316,9 +3316,9 @@
 LY_ERR
 lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node)
 {
-    LY_ARRAY_COUNT_TYPE u;
     const struct lyd_node_term **item = NULL;
     struct lyd_leafref_links_rec *rec;
+    LY_ARRAY_COUNT_TYPE u;
 
     assert(node);
     assert(leafref_node);
@@ -3327,6 +3327,7 @@
         return LY_EDENIED;
     }
 
+    /* add leafref node into the list of target node */
     LY_CHECK_RET(lyd_get_or_create_leafref_links_record(node, &rec, 1));
     LY_ARRAY_FOR(rec->leafref_nodes, u) {
         if (rec->leafref_nodes[u] == leafref_node) {
@@ -3336,8 +3337,18 @@
 
     LY_ARRAY_NEW_RET(LYD_CTX(node), rec->leafref_nodes, item, LY_EMEM);
     *item = leafref_node;
+
+    /* add target node into the list of leafref node*/
     LY_CHECK_RET(lyd_get_or_create_leafref_links_record(leafref_node, &rec, 1));
-    rec->target_node = node;
+    LY_ARRAY_FOR(rec->target_nodes, u) {
+        if (rec->target_nodes[u] == node) {
+            return LY_SUCCESS;
+        }
+    }
+
+    LY_ARRAY_NEW_RET(LYD_CTX(node), rec->target_nodes, item, LY_EMEM);
+    *item = node;
+
     return LY_SUCCESS;
 }
 
@@ -3345,11 +3356,13 @@
 lyd_leafref_link_node_tree(const struct lyd_node *tree)
 {
     const struct lyd_node *sibling, *elem;
-    struct lyd_node *target;
+    struct ly_set *targets = NULL;
     char *errmsg;
     struct lyd_node_term *leafref_node;
     struct lysc_node_leaf *leaf_schema;
     struct lysc_type_leafref *lref;
+    LY_ERR ret = LY_SUCCESS;
+    uint32_t i;
 
     LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);
 
@@ -3365,12 +3378,18 @@
 
                 if (leaf_schema->type->basetype == LY_TYPE_LEAFREF) {
                     lref = (struct lysc_type_leafref *)leaf_schema->type;
-                    if (lyplg_type_resolve_leafref(lref, elem, &leafref_node->value, tree, &target, &errmsg)) {
+                    ly_set_free(targets, NULL);
+                    if (lyplg_type_resolve_leafref(lref, elem, &leafref_node->value, tree, &targets, &errmsg)) {
                         /* leafref target not found */
                         free(errmsg);
                     } else {
                         /* leafref target found, link it */
-                        LY_CHECK_RET(lyd_link_leafref_node((struct lyd_node_term *)target, leafref_node));
+                        for (i = 0; i < targets->count; ++i) {
+                            if (targets->dnodes[i]->schema->nodetype & LYD_NODE_TERM) {
+                                ret = lyd_link_leafref_node((struct lyd_node_term *)targets->dnodes[i], leafref_node);
+                                LY_CHECK_GOTO(ret, cleanup);
+                            }
+                        }
                     }
                 }
             }
@@ -3378,7 +3397,9 @@
         }
     }
 
-    return LY_SUCCESS;
+cleanup:
+    ly_set_free(targets, NULL);
+    return ret;
 }
 
 LY_ERR
@@ -3394,20 +3415,22 @@
         return LY_EDENIED;
     }
 
+    /* remove link from target node to leafref node */
     ret = lyd_get_or_create_leafref_links_record(node, &rec, 0);
     if (ret == LY_SUCCESS) {
         LY_ARRAY_REMOVE_VALUE(rec->leafref_nodes, leafref_node);
-        if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (rec->target_node == NULL)) {
+        if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (LY_ARRAY_COUNT(rec->target_nodes) == 0)) {
             lyd_free_leafref_nodes(node);
         }
     } else if (ret != LY_ENOTFOUND) {
         return ret;
     }
 
+    /* remove link from leafref node to target node */
     ret = lyd_get_or_create_leafref_links_record(leafref_node, &rec, 0);
     if (ret == LY_SUCCESS) {
-        rec->target_node = NULL;
-        if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (rec->target_node == NULL)) {
+        LY_ARRAY_REMOVE_VALUE(rec->target_nodes, node);
+        if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (LY_ARRAY_COUNT(rec->target_nodes) == 0)) {
             lyd_free_leafref_nodes(leafref_node);
         }
     } else if (ret != LY_ENOTFOUND) {
diff --git a/src/tree_data.h b/src/tree_data.h
index 0cce979..745496d 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -1008,9 +1008,9 @@
                                                     It can also be populated based on manual request using
                                                     [link api](@ref lyd_leafref_link_node_tree). Freeing of the resources is
                                                     automatic. */
-    const struct lyd_node_term *target_node;    /** pointer to leafref target data node, by default is NULL. The logic
-                                                    is the same as for [leafref_nodes](@ref leafref_nodes) and is filled only
-                                                    for leafrefs */
+    const struct lyd_node_term **target_nodes;  /** list of leafref target data nodes [sized array](@ref sizedarrays)). Byt default
+                                                    it is empty. The logic is the same as for [leafref_nodes](@ref leafref_nodes) and
+                                                    is filled only for leafrefs */
 };
 
 /**
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index ce4e526..80a02f2 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -142,15 +142,15 @@
 lyd_free_leafref_links_rec(struct lyd_leafref_links_rec *rec)
 {
     LY_ARRAY_COUNT_TYPE u;
-    struct lyd_leafref_links_rec *leafref_rec;
+    struct lyd_leafref_links_rec *rec2;
 
     assert(rec);
 
-    /* remove stored leafref nodes */
+    /* remove links of leafref nodes */
     LY_ARRAY_FOR(rec->leafref_nodes, u) {
-        if (lyd_get_or_create_leafref_links_record(rec->leafref_nodes[u], &leafref_rec, 0) == LY_SUCCESS) {
-            leafref_rec->target_node = NULL;
-            if (!LY_ARRAY_COUNT(leafref_rec->leafref_nodes) && !leafref_rec->target_node) {
+        if (lyd_get_or_create_leafref_links_record(rec->leafref_nodes[u], &rec2, 0) == LY_SUCCESS) {
+            LY_ARRAY_REMOVE_VALUE(rec2->target_nodes, rec->node);
+            if ((LY_ARRAY_COUNT(rec2->leafref_nodes) == 0) && (LY_ARRAY_COUNT(rec2->target_nodes) == 0)) {
                 lyd_free_leafref_nodes(rec->leafref_nodes[u]);
             }
         }
@@ -158,10 +158,17 @@
     LY_ARRAY_FREE(rec->leafref_nodes);
     rec->leafref_nodes = NULL;
 
-    /* remove stored target node */
-    if (rec->target_node) {
-        lyd_unlink_leafref_node(rec->target_node, rec->node);
+    /* remove links of target nodes */
+    LY_ARRAY_FOR(rec->target_nodes, u) {
+        if (lyd_get_or_create_leafref_links_record(rec->target_nodes[u], &rec2, 0) == LY_SUCCESS) {
+            LY_ARRAY_REMOVE_VALUE(rec2->leafref_nodes, rec->node);
+            if ((LY_ARRAY_COUNT(rec2->leafref_nodes) == 0) && (LY_ARRAY_COUNT(rec2->target_nodes) == 0)) {
+                lyd_free_leafref_nodes(rec->target_nodes[u]);
+            }
+        }
     }
+    LY_ARRAY_FREE(rec->target_nodes);
+    rec->target_nodes = NULL;
 }
 
 void
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index d639591..4df6d60 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -617,7 +617,7 @@
 LY_ERR lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct lyd_leafref_links_rec **record, ly_bool create);
 
 /**
- * @brief Adds links between leafref adn data node.
+ * @brief Adds links between leafref and data node.
  *
  * If the links were already added, it will not be added again.
  * This API requires usage of LY_CTX_LEAFREF_LINKING context flag.
@@ -630,7 +630,7 @@
 LY_ERR lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node);
 
 /**
- * @brief Removes links between leafref adn data node.
+ * @brief Removes links between leafref and data node.
  *
  * If the links were never added, it will be silently ignored.
  * This API requires usage of LY_CTX_LEAFREF_LINKING context flag.
diff --git a/src/tree_edit.h b/src/tree_edit.h
index 113c9e3..af13fd4 100644
--- a/src/tree_edit.h
+++ b/src/tree_edit.h
@@ -241,9 +241,9 @@
     { \
         LY_ARRAY_COUNT_TYPE index__; \
         LY_ARRAY_FOR(ARRAY, index__) { \
-            if (ARRAY[index__] == VALUE) { \
+            if ((ARRAY)[index__] == VALUE) { \
                 if (index__ != LY_ARRAY_COUNT(ARRAY) - 1) { \
-                    memmove(&(ARRAY[index__]), &(ARRAY[LY_ARRAY_COUNT(ARRAY) - 1]), sizeof *(ARRAY)); \
+                    memmove(&((ARRAY)[index__]), &((ARRAY)[LY_ARRAY_COUNT(ARRAY) - 1]), sizeof *(ARRAY)); \
                 } \
                 LY_ARRAY_DECREMENT(ARRAY); \
                 break; \
diff --git a/src/xpath.c b/src/xpath.c
index bc5f695..0f0869e 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -4017,6 +4017,9 @@
     char *errmsg = NULL;
     uint8_t oper;
     LY_ERR r;
+    LY_ERR ret = LY_SUCCESS;
+    struct ly_set *targets = NULL;
+    uint32_t i;
 
     if (options & LYXP_SCNODE_ALL) {
         if (args[0]->type != LYXP_SET_SCNODE_SET) {
@@ -4048,12 +4051,14 @@
             } /* else the target was found before but is disabled so it was removed */
         }
 
-        return LY_SUCCESS;
+        ret = LY_SUCCESS;
+        goto cleanup;
     }
 
     if (args[0]->type != LYXP_SET_NODE_SET) {
         LOGVAL(set->ctx, LY_VCODE_XP_INARGTYPE, 1, print_set_type(args[0]), "deref(node-set)");
-        return LY_EVALID;
+        ret = LY_EVALID;
+        goto cleanup;
     }
 
     lyxp_set_free_content(set);
@@ -4063,27 +4068,37 @@
         if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
             if (sleaf->type->basetype == LY_TYPE_LEAFREF) {
                 /* find leafref target */
-                if (lyplg_type_resolve_leafref((struct lysc_type_leafref *)sleaf->type, &leaf->node, &leaf->value, set->tree,
-                        &node, &errmsg)) {
+                r = lyplg_type_resolve_leafref((struct lysc_type_leafref *)sleaf->type, &leaf->node, &leaf->value, set->tree,
+                        &targets, &errmsg);
+                if (r) {
                     LOGERR(set->ctx, LY_EVALID, "%s", errmsg);
                     free(errmsg);
-                    return LY_EVALID;
+                    ret = LY_EVALID;
+                    goto cleanup;
+                }
+
+                /* insert nodes into set */
+                for (i = 0; i < targets->count; ++i) {
+                    set_insert_node(set, targets->dnodes[i], 0, LYXP_NODE_ELEM, 0);
                 }
             } else {
                 assert(sleaf->type->basetype == LY_TYPE_INST);
                 if (ly_path_eval(leaf->value.target, set->tree, NULL, &node)) {
                     LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.",
                             lyd_get_value(&leaf->node));
-                    return LY_EVALID;
+                    ret = LY_EVALID;
+                    goto cleanup;
                 }
-            }
 
-            /* insert it */
-            set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
+                /* insert it */
+                set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
+            }
         }
     }
 
-    return LY_SUCCESS;
+cleanup:
+    ly_set_free(targets, NULL);
+    return ret;
 }
 
 static LY_ERR