plugins ext FEATURE initial schema-mount support

Only for XML data for now. Includes lots of other
changes needed to support this extension.
diff --git a/src/tree_data.c b/src/tree_data.c
index ab4c495..893bdd0 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -346,6 +346,7 @@
     struct ly_set parsed = {0};
     struct lyd_node *first;
     uint32_t i;
+    ly_bool subtree_sibling = 0;
 
     assert(ctx && (parent || first_p));
 
@@ -360,13 +361,16 @@
     /* parse the data */
     switch (format) {
     case LYD_XML:
-        rc = lyd_parse_xml(ctx, ext, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_DATA_YANG, NULL, &parsed, &lydctx);
+        rc = lyd_parse_xml(ctx, ext, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_DATA_YANG, NULL, &parsed,
+                &subtree_sibling, &lydctx);
         break;
     case LYD_JSON:
-        rc = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_DATA_YANG, &parsed, &lydctx);
+        rc = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_DATA_YANG, &parsed,
+                &subtree_sibling, &lydctx);
         break;
     case LYD_LYB:
-        rc = lyd_parse_lyb(ctx, ext, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_DATA_YANG, &parsed, &lydctx);
+        rc = lyd_parse_lyb(ctx, ext, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_DATA_YANG, &parsed,
+                &subtree_sibling, &lydctx);
         break;
     case LYD_UNKNOWN:
         LOGARG(ctx, format);
@@ -384,8 +388,8 @@
 
     if (!(parse_opts & LYD_PARSE_ONLY)) {
         /* validate data */
-        rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_exts, &lydctx->node_types,
-                &lydctx->meta_types, NULL);
+        rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
+                &lydctx->ext_val, NULL);
         LY_CHECK_GOTO(rc, cleanup);
     }
 
@@ -409,6 +413,8 @@
             lyd_free_all(*first_p);
             *first_p = NULL;
         }
+    } else if (subtree_sibling) {
+        rc = LY_ENOT;
     }
     ly_set_erase(&parsed, NULL);
     return rc;
@@ -558,7 +564,7 @@
     /* parse the data */
     switch (format) {
     case LYD_XML:
-        rc = lyd_parse_xml(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+        rc = lyd_parse_xml(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, NULL, &lydctx);
         if (rc && envp) {
             /* special situation when the envelopes were parsed successfully */
             if (tree) {
@@ -569,10 +575,10 @@
         }
         break;
     case LYD_JSON:
-        rc = lyd_parse_json(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &parsed, &lydctx);
+        rc = lyd_parse_json(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &parsed, NULL, &lydctx);
         break;
     case LYD_LYB:
-        rc = lyd_parse_lyb(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &parsed, &lydctx);
+        rc = lyd_parse_lyb(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &parsed, NULL, &lydctx);
         break;
     case LYD_UNKNOWN:
         LOGARG(ctx, format);
@@ -1310,7 +1316,7 @@
     }
 
     return lyd_create_meta(parent, meta, module, name, name_len, val_str, strlen(val_str), NULL, LY_VALUE_JSON,
-            NULL, LYD_HINT_DATA, clear_dflt, NULL);
+            NULL, LYD_HINT_DATA, parent ? parent->schema : NULL, clear_dflt, NULL);
 }
 
 LIBYANG_API_DEF LY_ERR
@@ -1350,7 +1356,7 @@
     }
 
     return lyd_create_meta(parent, meta, mod, attr->name.name, strlen(attr->name.name), attr->value, strlen(attr->value),
-            NULL, attr->format, attr->val_prefix_data, attr->hints, clear_dflt, NULL);
+            NULL, attr->format, attr->val_prefix_data, attr->hints, parent ? parent->schema : NULL, clear_dflt, NULL);
 }
 
 LIBYANG_API_DEF LY_ERR
@@ -1635,8 +1641,9 @@
     }
 
     /* parse the new value into a new meta structure */
-    LY_CHECK_GOTO(ret = lyd_create_meta(NULL, &m2, meta->annotation->module, meta->name, strlen(meta->name), val_str,
-            strlen(val_str), NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, 0, NULL), cleanup);
+    ret = lyd_create_meta(NULL, &m2, meta->annotation->module, meta->name, strlen(meta->name), val_str, strlen(val_str),
+            NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, meta->parent ? meta->parent->schema : NULL, 0, NULL);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* compare original and new value */
     if (lyd_compare_meta(meta, m2)) {
@@ -2089,8 +2096,8 @@
 
 LY_ERR
 lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
-        const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_exts, struct ly_set *node_types,
-        uint32_t impl_opts, struct lyd_node **diff)
+        const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, uint32_t impl_opts,
+        struct lyd_node **diff)
 {
     LY_ERR ret;
     const struct lysc_node *iter = NULL;
@@ -2123,11 +2130,11 @@
             if (!node && ((struct lysc_node_choice *)iter)->dflt) {
                 /* create default case data */
                 LY_CHECK_RET(lyd_new_implicit_r(parent, first, &((struct lysc_node_choice *)iter)->dflt->node,
-                        NULL, node_when, node_exts, node_types, impl_opts, diff));
+                        NULL, node_when, node_types, impl_opts, diff));
             } else if (node) {
                 /* create any default data in the existing case */
                 assert(node->schema->parent->nodetype == LYS_CASE);
-                LY_CHECK_RET(lyd_new_implicit_r(parent, first, node->schema->parent, NULL, node_when, node_exts, node_types,
+                LY_CHECK_RET(lyd_new_implicit_r(parent, first, node->schema->parent, NULL, node_when, node_types,
                         impl_opts, diff));
             }
             break;
@@ -2142,17 +2149,13 @@
                     /* remember to resolve when */
                     LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
                 }
-                if (node_exts) {
-                    /* remember to call all the extension's validation callbacks */
-                    LY_CHECK_RET(lysc_node_ext_tovalidate(node_exts, node));
-                }
                 if (diff) {
                     /* add into diff */
                     LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
                 }
 
                 /* create any default children */
-                LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, node_when, node_exts, node_types,
+                LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, node_when, node_types,
                         impl_opts, diff));
             }
             break;
@@ -2176,10 +2179,6 @@
                     /* remember to resolve when */
                     LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
                 }
-                if (node_exts) {
-                    /* remember to call all the extension's validation callbacks */
-                    LY_CHECK_RET(lysc_node_ext_tovalidate(node_exts, node));
-                }
                 if (diff) {
                     /* add into diff */
                     LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
@@ -2208,10 +2207,6 @@
                         /* remember to resolve when */
                         LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
                     }
-                    if (node_exts) {
-                        /* remember to call all the extension's validation callbacks */
-                        LY_CHECK_RET(lysc_node_ext_tovalidate(node_exts, node));
-                    }
                     if (diff) {
                         /* add into diff */
                         LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
@@ -2233,7 +2228,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_node *node;
-    struct ly_set node_when = {0}, node_exts = {0};
+    struct ly_set node_when = {0};
 
     LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);
     if (diff) {
@@ -2244,20 +2239,18 @@
         /* skip added default nodes */
         if (((node->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) &&
                 (node->schema->nodetype & LYD_NODE_INNER)) {
-            LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &node_when, &node_exts,
-                    NULL, implicit_options, diff), cleanup);
+            LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &node_when, NULL,
+                    implicit_options, diff), cleanup);
         }
 
         LYD_TREE_DFS_END(tree, node);
     }
 
     /* resolve when and remove any invalid defaults */
-    LY_CHECK_GOTO(ret = lyd_validate_unres(&tree, NULL, &node_when, LYXP_IGNORE_WHEN, &node_exts, NULL, NULL, diff),
-            cleanup);
+    LY_CHECK_GOTO(ret = lyd_validate_unres(&tree, NULL, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff), cleanup);
 
 cleanup:
     ly_set_erase(&node_when, NULL);
-    ly_set_erase(&node_exts, NULL);
     if (ret && diff) {
         lyd_free_all(*diff);
         *diff = NULL;
@@ -2311,7 +2304,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_node *root, *d = NULL;
-    struct ly_set node_when = {0}, node_exts = {0};
+    struct ly_set node_when = {0};
 
     LY_CHECK_ARG_RET(NULL, tree, module, LY_EINVAL);
     LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module ? module->ctx : NULL, LY_EINVAL);
@@ -2320,10 +2313,10 @@
     }
 
     /* add all top-level defaults for this module */
-    LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, &node_when, &node_exts, NULL, implicit_options, diff), cleanup);
+    LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, &node_when, NULL, implicit_options, diff), cleanup);
 
     /* resolve when and remove any invalid defaults */
-    LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, &node_when, LYXP_IGNORE_WHEN, &node_exts, NULL, NULL, diff),
+    LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff),
             cleanup);
 
     /* process nested nodes */
@@ -2343,7 +2336,6 @@
 
 cleanup:
     ly_set_erase(&node_when, NULL);
-    ly_set_erase(&node_exts, NULL);
     if (ret && diff) {
         lyd_free_all(*diff);
         *diff = NULL;
@@ -2682,7 +2674,7 @@
     LY_CHECK_RET(lyd_insert_check_schema(parent->schema, NULL, node->schema));
 
     if (node->schema && (node->schema->flags & LYS_KEY)) {
-        LOGERR(parent->schema->module->ctx, LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+        LOGERR(LYD_CTX(parent), LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
         return LY_EINVAL;
     }
 
@@ -2700,6 +2692,28 @@
 }
 
 LIBYANG_API_DEF LY_ERR
+lyd_insert_ext(struct lyd_node *parent, struct lyd_node *first)
+{
+    struct lyd_node *iter;
+
+    LY_CHECK_ARG_RET(NULL, parent, first, !first->parent, !first->prev->next,
+            !parent->schema || (parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL);
+
+    if (first->schema && (first->schema->flags & LYS_KEY)) {
+        LOGERR(LYD_CTX(parent), LY_EINVAL, "Cannot insert key \"%s\".", first->schema->name);
+        return LY_EINVAL;
+    }
+
+    while (first) {
+        iter = first->next;
+        lyd_unlink_tree(first);
+        lyd_insert_node(parent, NULL, first, 0);
+        first = iter;
+    }
+    return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
 lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_node **first)
 {
     struct lyd_node *iter;
@@ -2886,7 +2900,7 @@
 LY_ERR
 lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
         size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
-        void *prefix_data, uint32_t hints, ly_bool clear_dflt, ly_bool *incomplete)
+        void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool clear_dflt, ly_bool *incomplete)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_ext_instance *ant = NULL;
@@ -2896,7 +2910,7 @@
 
     assert((parent || meta) && mod);
 
-    LOG_LOCSET(parent ? parent->schema : NULL, parent, NULL, NULL);
+    LOG_LOCSET(ctx_node, parent, NULL, NULL);
 
     LY_ARRAY_FOR(mod->compiled->exts, u) {
         if ((mod->compiled->exts[u].def->plugin == lyplg_find(LYPLG_EXTENSION, LYEXT_PLUGIN_INTERNAL_ANNOTATION)) &&
@@ -2920,7 +2934,7 @@
     mt->annotation = ant;
     ant_type = ant->substmts[ANNOTATION_SUBSTMT_TYPE].storage;
     ret = lyd_value_store(mod->ctx, &mt->value, *ant_type, value, value_len, dynamic, format, prefix_data, hints,
-            parent ? parent->schema : NULL, incomplete);
+            ctx_node, incomplete);
     LY_CHECK_ERR_GOTO(ret, free(mt), cleanup);
     ret = lydict_insert(mod->ctx, name, name_len, &mt->name);
     LY_CHECK_ERR_GOTO(ret, free(mt), cleanup);
@@ -2938,7 +2952,7 @@
     }
 
 cleanup:
-    LOG_LOCBACK((parent && parent->schema) ? 1 : 0, parent ? 1 : 0, 0, 0);
+    LOG_LOCBACK(ctx_node ? 1 : 0, parent ? 1 : 0, 0, 0);
     return ret;
 }
 
@@ -3396,34 +3410,88 @@
 }
 
 /**
+ * @brief Find @p schema equivalent in @p trg_ctx.
+ *
+ * @param[in] schema Schema node to find.
+ * @param[in] trg_ctx Target context to search in.
+ * @param[in] parent Data parent of @p schema, if any.
+ * @param[out] trg_schema Found schema from @p trg_ctx to use.
+ * @return LY_RRR value.
+ */
+static LY_ERR
+lyd_dup_find_schema(const struct lysc_node *schema, const struct ly_ctx *trg_ctx, struct lyd_node *parent,
+        const struct lysc_node **trg_schema)
+{
+    const struct lysc_node *node = NULL, *sparent = NULL;
+    const struct lys_module *mod = NULL;
+    char *path;
+
+    if (!schema) {
+        /* opaque node */
+        *trg_schema = NULL;
+        return LY_SUCCESS;
+    }
+
+    if (parent && parent->schema) {
+        /* start from schema parent */
+        sparent = parent->schema;
+    } else {
+        /* start from module */
+        mod = ly_ctx_get_module_implemented(trg_ctx, schema->module->name);
+        if (!mod) {
+            LOGERR(trg_ctx, LY_ENOTFOUND, "Module \"%s\" not present/implemented in the target context.",
+                    schema->module->name);
+            return LY_ENOTFOUND;
+        }
+    }
+
+    /* find the schema node */
+    while ((node = lys_getnext(node, sparent, mod ? mod->compiled : NULL, 0))) {
+        if (!strcmp(node->module->name, schema->module->name) && !strcmp(node->name, schema->name)) {
+            *trg_schema = node;
+            return LY_SUCCESS;
+        }
+    }
+
+    /* schema node not found */
+    path = lysc_path(schema, LYSC_PATH_LOG, NULL, 0);
+    LOGERR(trg_ctx, LY_ENOTFOUND, "Schema node \"%s\" not found in the target context.", path);
+    free(path);
+    return LY_ENOTFOUND;
+}
+
+/**
  * @brief Duplicate a single node and connect it into @p parent (if present) or last of @p first siblings.
  *
- * Ignores LYD_DUP_WITH_PARENTS and LYD_DUP_WITH_SIBLINGS which are supposed to be handled by lyd_dup().
+ * Ignores ::LYD_DUP_WITH_PARENTS and ::LYD_DUP_WITH_SIBLINGS which are supposed to be handled by lyd_dup().
  *
- * @param[in] node Original node to duplicate
+ * @param[in] node Node to duplicate.
+ * @param[in] trg_ctx Target context for duplicated nodes.
  * @param[in] parent Parent to insert into, NULL for top-level sibling.
  * @param[in] insert_last Whether the duplicated node can be inserted as the last child of @p parent. Set for
  * recursive duplication as an optimization.
  * @param[in,out] first First sibling, NULL if no top-level sibling exist yet. Can be also NULL if @p parent is set.
  * @param[in] options Bitmask of options flags, see @ref dupoptions.
- * @param[out] dup_p Pointer where the created duplicated node is placed (besides connecting it int @p parent / @p first sibling).
- * @return LY_ERR value
+ * @param[out] dup_p Pointer where the created duplicated node is placed (besides connecting it to @p parent / @p first).
+ * @return LY_ERR value.
  */
 static LY_ERR
-lyd_dup_r(const struct lyd_node *node, struct lyd_node *parent, ly_bool insert_last, struct lyd_node **first,
-        uint32_t options, struct lyd_node **dup_p)
+lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, ly_bool insert_last,
+        struct lyd_node **first, uint32_t options, struct lyd_node **dup_p)
 {
     LY_ERR ret;
     struct lyd_node *dup = NULL;
     struct lyd_meta *meta;
     struct lyd_attr *attr;
     struct lyd_node_any *any;
+    const struct lysc_type *type;
+    const char *val_can;
 
     LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
 
     if (!node->schema) {
         dup = calloc(1, sizeof(struct lyd_node_opaq));
-        ((struct lyd_node_opaq *)dup)->ctx = LYD_CTX(node);
+        ((struct lyd_node_opaq *)dup)->ctx = trg_ctx;
     } else {
         switch (node->schema->nodetype) {
         case LYS_RPC:
@@ -3442,19 +3510,23 @@
             dup = calloc(1, sizeof(struct lyd_node_any));
             break;
         default:
-            LOGINT(LYD_CTX(node));
+            LOGINT(trg_ctx);
             ret = LY_EINT;
             goto error;
         }
     }
-    LY_CHECK_ERR_GOTO(!dup, LOGMEM(LYD_CTX(node)); ret = LY_EMEM, error);
+    LY_CHECK_ERR_GOTO(!dup, LOGMEM(trg_ctx); ret = LY_EMEM, error);
 
     if (options & LYD_DUP_WITH_FLAGS) {
         dup->flags = node->flags;
     } else {
         dup->flags = (node->flags & LYD_DEFAULT) | LYD_NEW;
     }
-    dup->schema = node->schema;
+    if (trg_ctx == LYD_CTX(node)) {
+        dup->schema = node->schema;
+    } else {
+        LY_CHECK_GOTO(ret = lyd_dup_find_schema(node->schema, trg_ctx, parent, &dup->schema), error);
+    }
     dup->prev = dup;
 
     /* duplicate metadata/attributes */
@@ -3479,17 +3551,17 @@
         if (options & LYD_DUP_RECURSIVE) {
             /* duplicate all the children */
             LY_LIST_FOR(orig->child, child) {
-                LY_CHECK_GOTO(ret = lyd_dup_r(child, dup, 1, NULL, options, NULL), error);
+                LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error);
             }
         }
-        LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), orig->name.name, 0, &opaq->name.name), error);
-        LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), orig->name.prefix, 0, &opaq->name.prefix), error);
-        LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), orig->name.module_ns, 0, &opaq->name.module_ns), error);
-        LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), orig->value, 0, &opaq->value), error);
+        LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.name, 0, &opaq->name.name), error);
+        LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.prefix, 0, &opaq->name.prefix), error);
+        LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.module_ns, 0, &opaq->name.module_ns), error);
+        LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->value, 0, &opaq->value), error);
         opaq->hints = orig->hints;
         opaq->format = orig->format;
         if (orig->val_prefix_data) {
-            ret = ly_dup_prefix_data(LYD_CTX(node), opaq->format, orig->val_prefix_data, &opaq->val_prefix_data);
+            ret = ly_dup_prefix_data(trg_ctx, opaq->format, orig->val_prefix_data, &opaq->val_prefix_data);
             LY_CHECK_GOTO(ret, error);
         }
     } else if (dup->schema->nodetype & LYD_NODE_TERM) {
@@ -3497,8 +3569,17 @@
         struct lyd_node_term *orig = (struct lyd_node_term *)node;
 
         term->hash = orig->hash;
-        LY_CHECK_ERR_GOTO(orig->value.realtype->plugin->duplicate(LYD_CTX(node), &orig->value, &term->value),
-                LOGERR(LYD_CTX(node), LY_EINT, "Value duplication failed."); ret = LY_EINT, error);
+        if (trg_ctx == LYD_CTX(node)) {
+            ret = orig->value.realtype->plugin->duplicate(trg_ctx, &orig->value, &term->value);
+            LY_CHECK_ERR_GOTO(ret, LOGERR(trg_ctx, ret, "Value duplication failed."), error);
+        } else {
+            /* store canonical value in the target context */
+            val_can = lyd_get_value(node);
+            type = ((struct lysc_node_leaf *)term->schema)->type;
+            ret = lyd_value_store(trg_ctx, &term->value, type, val_can, strlen(val_can), NULL, LY_VALUE_CANON, NULL,
+                    LYD_HINT_DATA, term->schema, NULL);
+            LY_CHECK_GOTO(ret, error);
+        }
     } else if (dup->schema->nodetype & LYD_NODE_INNER) {
         struct lyd_node_inner *orig = (struct lyd_node_inner *)node;
         struct lyd_node *child;
@@ -3506,7 +3587,7 @@
         if (options & LYD_DUP_RECURSIVE) {
             /* duplicate all the children */
             LY_LIST_FOR(orig->child, child) {
-                LY_CHECK_GOTO(ret = lyd_dup_r(child, dup, 1, NULL, options, NULL), error);
+                LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error);
             }
         } else if ((dup->schema->nodetype == LYS_LIST) && !(dup->schema->flags & LYS_KEYLESS)) {
             /* always duplicate keys of a list */
@@ -3522,7 +3603,7 @@
                      * but there can be also some non-key nodes */
                     continue;
                 }
-                LY_CHECK_GOTO(ret = lyd_dup_r(child, dup, 1, NULL, options, NULL), error);
+                LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error);
                 child = child->next;
             }
         }
@@ -3550,6 +3631,7 @@
  * @brief Get a parent node to connect duplicated subtree to.
  *
  * @param[in] node Node (subtree) to duplicate.
+ * @param[in] trg_ctx Target context for duplicated nodes.
  * @param[in] parent Initial parent to connect to.
  * @param[in] options Bitmask of options flags, see @ref dupoptions.
  * @param[out] dup_parent First duplicated parent node, if any.
@@ -3557,8 +3639,8 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyd_dup_get_local_parent(const struct lyd_node *node, const struct lyd_node_inner *parent, uint32_t options,
-        struct lyd_node **dup_parent, struct lyd_node_inner **local_parent)
+lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, const struct lyd_node_inner *parent,
+        uint32_t options, struct lyd_node **dup_parent, struct lyd_node_inner **local_parent)
 {
     const struct lyd_node_inner *orig_parent, *iter;
     ly_bool repeat = 1;
@@ -3573,7 +3655,7 @@
             repeat = 0;
         } else {
             iter = NULL;
-            LY_CHECK_RET(lyd_dup_r((struct lyd_node *)orig_parent, NULL, 0, (struct lyd_node **)&iter, options,
+            LY_CHECK_RET(lyd_dup_r((struct lyd_node *)orig_parent, trg_ctx, NULL, 0, (struct lyd_node **)&iter, options,
                     (struct lyd_node **)&iter));
         }
         if (!*local_parent) {
@@ -3598,7 +3680,7 @@
 
     if (repeat && parent) {
         /* given parent and created parents chain actually do not interconnect */
-        LOGERR(LYD_CTX(node), LY_EINVAL,
+        LOGERR(trg_ctx, LY_EINVAL,
                 "Invalid argument parent (%s()) - does not interconnect with the created node's parents chain.", __func__);
         return LY_EINVAL;
     }
@@ -3607,8 +3689,8 @@
 }
 
 static LY_ERR
-lyd_dup(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options, ly_bool nosiblings,
-        struct lyd_node **dup)
+lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent, uint32_t options,
+        ly_bool nosiblings, struct lyd_node **dup)
 {
     LY_ERR rc;
     const struct lyd_node *orig;          /* original node to be duplicated */
@@ -3616,10 +3698,10 @@
     struct lyd_node *top = NULL;          /* the most higher created node */
     struct lyd_node_inner *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
 
-    LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
+    assert(node && trg_ctx && (!parent || (LYD_CTX(parent) == trg_ctx)));
 
     if (options & LYD_DUP_WITH_PARENTS) {
-        LY_CHECK_GOTO(rc = lyd_dup_get_local_parent(node, parent, options & (LYD_DUP_WITH_FLAGS | LYD_DUP_NO_META),
+        LY_CHECK_GOTO(rc = lyd_dup_get_local_parent(node, trg_ctx, parent, options & (LYD_DUP_WITH_FLAGS | LYD_DUP_NO_META),
                 &top, &local_parent), error);
     } else {
         local_parent = parent;
@@ -3630,16 +3712,17 @@
             if (local_parent) {
                 /* the key must already exist in the parent */
                 rc = lyd_find_sibling_schema(local_parent->child, orig->schema, first ? NULL : &first);
-                LY_CHECK_ERR_GOTO(rc, LOGINT(LYD_CTX(node)), error);
+                LY_CHECK_ERR_GOTO(rc, LOGINT(trg_ctx), error);
             } else {
                 assert(!(options & LYD_DUP_WITH_PARENTS));
                 /* duplicating a single key, okay, I suppose... */
-                rc = lyd_dup_r(orig, NULL, 0, &first, options, first ? NULL : &first);
+                rc = lyd_dup_r(orig, trg_ctx, NULL, 0, &first, options, first ? NULL : &first);
                 LY_CHECK_GOTO(rc, error);
             }
         } else {
             /* if there is no local parent, it will be inserted into first */
-            rc = lyd_dup_r(orig, local_parent ? &local_parent->node : NULL, 0, &first, options, first ? NULL : &first);
+            rc = lyd_dup_r(orig, trg_ctx, local_parent ? &local_parent->node : NULL, 0, &first, options,
+                    first ? NULL : &first);
             LY_CHECK_GOTO(rc, error);
         }
         if (nosiblings) {
@@ -3664,13 +3747,43 @@
 LIBYANG_API_DEF LY_ERR
 lyd_dup_single(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options, struct lyd_node **dup)
 {
-    return lyd_dup(node, parent, options, 1, dup);
+    LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
+    if (node && parent && (LYD_CTX(node) != LYD_CTX(parent))) {
+        LOGERR(NULL, LY_EINVAL, "Different contexts used in node duplication.");
+        return LY_EINVAL;
+    }
+
+    return lyd_dup(node, LYD_CTX(node), parent, options, 1, dup);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent,
+        uint32_t options, struct lyd_node **dup)
+{
+    LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL);
+
+    return lyd_dup(node, trg_ctx, parent, options, 1, dup);
 }
 
 LIBYANG_API_DEF LY_ERR
 lyd_dup_siblings(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options, struct lyd_node **dup)
 {
-    return lyd_dup(node, parent, options, 0, dup);
+    LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
+    if (node && parent && (LYD_CTX(node) != LYD_CTX(parent))) {
+        LOGERR(NULL, LY_EINVAL, "Different contexts used in node duplication.");
+        return LY_EINVAL;
+    }
+
+    return lyd_dup(node, LYD_CTX(node), parent, options, 0, dup);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent,
+        uint32_t options, struct lyd_node **dup)
+{
+    LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL);
+
+    return lyd_dup(node, trg_ctx, parent, options, 0, dup);
 }
 
 LIBYANG_API_DEF LY_ERR
@@ -4461,24 +4574,24 @@
 }
 
 LIBYANG_API_DEF LY_ERR
-lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath,
-        const struct lyxp_var *vars, struct ly_set **set)
+lyd_find_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, LY_VALUE_FORMAT format,
+        void *prefix_data, const struct lyxp_var *vars, struct ly_set **set)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyxp_set xp_set = {0};
     struct lyxp_expr *exp = NULL;
     uint32_t i;
 
-    LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL);
+    LY_CHECK_ARG_RET(NULL, tree, xpath, format, set, LY_EINVAL);
 
     *set = NULL;
 
-    /* compile expression */
+    /* parse expression */
     ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(tree), xpath, 0, 1, &exp);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* evaluate expression */
-    ret = lyxp_eval(LYD_CTX(tree), exp, NULL, LY_VALUE_JSON, NULL, ctx_node, tree, vars, &xp_set, LYXP_IGNORE_WHEN);
+    ret = lyxp_eval(LYD_CTX(tree), exp, NULL, format, prefix_data, ctx_node, tree, vars, &xp_set, LYXP_IGNORE_WHEN);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* allocate return set */
@@ -4511,11 +4624,20 @@
 }
 
 LIBYANG_API_DEF LY_ERR
+lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath,
+        const struct lyxp_var *vars, struct ly_set **set)
+{
+    LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL);
+
+    return lyd_find_xpath4(ctx_node, tree, xpath, LY_VALUE_JSON, NULL, vars, set);
+}
+
+LIBYANG_API_DEF LY_ERR
 lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, struct ly_set **set)
 {
     LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
 
-    return lyd_find_xpath3(ctx_node, ctx_node, xpath, vars, set);
+    return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set);
 }
 
 LIBYANG_API_DEF LY_ERR
@@ -4523,7 +4645,7 @@
 {
     LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
 
-    return lyd_find_xpath3(ctx_node, ctx_node, xpath, NULL, set);
+    return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set);
 }
 
 LIBYANG_API_DEF LY_ERR