data tree FEATURE support for NETCONF messages
diff --git a/src/tree_data.c b/src/tree_data.c
index 4230160..dbb5c94 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -295,102 +295,117 @@
     return format;
 }
 
-API LY_ERR
-lyd_parse_data(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options,
-        struct lyd_node **tree)
+/**
+ * @brief Parse YANG data into a data tree.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input handle to read the input from.
+ * @param[in] format Expected format of the data in @p in.
+ * @param[in] parse_opts Options for parser.
+ * @param[in] val_opts Options for validation.
+ * @param[out] op Optional pointer to the parsed operation, if any.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_parse(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in,
+        LYD_FORMAT format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **op)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR rc = LY_SUCCESS;
     struct lyd_ctx *lydctx = NULL;
+    struct ly_set parsed = {0};
+    struct lyd_node *first;
+    uint32_t i;
 
-    LY_CHECK_ARG_RET(ctx, ctx, in, tree, LY_EINVAL);
-    LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL);
-    LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL);
+    assert(ctx && (parent || first_p));
 
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
-
-    /* init */
-    *tree = NULL;
+    if (first_p) {
+        *first_p = NULL;
+    }
 
     /* remember input position */
     in->func_start = in->current;
 
+    /* parse the data */
     switch (format) {
     case LYD_XML:
-        LY_CHECK_RET(lyd_parse_xml_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        rc = lyd_parse_xml(ctx, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_YANG_DATA, NULL, &parsed, &lydctx);
         break;
     case LYD_JSON:
-        LY_CHECK_RET(lyd_parse_json_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        rc = lyd_parse_json(ctx, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_YANG_DATA, &parsed, &lydctx);
         break;
     case LYD_LYB:
-        LY_CHECK_RET(lyd_parse_lyb_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        rc = lyd_parse_lyb(ctx, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_YANG_DATA, &parsed, &lydctx);
         break;
     case LYD_UNKNOWN:
-        LOGINT_RET(ctx);
+        LOGINT(ctx);
+        rc = LY_EINT;
+        break;
+    }
+    LY_CHECK_GOTO(rc, cleanup);
+
+    if (parent) {
+        /* get first top-level sibling */
+        for (first = parent; first->parent; first = lyd_parent(first)) {}
+        first = lyd_first_sibling(first);
+        first_p = &first;
     }
 
-    if (!(parse_options & LYD_PARSE_ONLY)) {
-        uint32_t i = 0;
-        const struct lys_module *mod;
-        struct lyd_node *first, *next, **first2;
+    if (!(parse_opts & LYD_PARSE_ONLY)) {
+        /* validate data */
+        rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types,
+                &lydctx->meta_types, NULL);
+        LY_CHECK_GOTO(rc, cleanup);
+    }
 
-        next = *tree;
-        while (1) {
-            if (validate_options & LYD_VALIDATE_PRESENT) {
-                mod = lyd_data_next_module(&next, &first);
-            } else {
-                mod = lyd_mod_next_module(next, NULL, ctx, &i, &first);
-            }
-            if (!mod) {
-                break;
-            }
-            if (!first || (first == *tree)) {
-                /* make sure first2 changes are carried to tree */
-                first2 = tree;
-            } else {
-                first2 = &first;
-            }
-
-            /* validate new top-level nodes, autodelete CANNOT occur, all nodes are new */
-            LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod, NULL), cleanup);
-
-            /* add all top-level defaults for this module */
-            ret = lyd_new_implicit_r(NULL, first2, NULL, mod, &lydctx->node_types, &lydctx->node_when,
-                    (validate_options & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
-
-            /* our first module node pointer may no longer be the first */
-            while (*first2 && (*first2)->prev->next && (lyd_owner_module(*first2) == lyd_owner_module((*first2)->prev))) {
-                *first2 = (*first2)->prev;
-            }
-
-            /* finish incompletely validated terminal values/attributes and when conditions */
-            ret = lyd_validate_unres(first2, mod, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
-
-            /* perform final validation that assumes the data tree is final */
-            LY_CHECK_GOTO(ret = lyd_validate_final_r(*first2, NULL, NULL, mod, validate_options, 0), cleanup);
-        }
+    /* set the operation node */
+    if (op) {
+        *op = lydctx->op_node;
     }
 
 cleanup:
-    lydctx->free(lydctx);
-    if (ret) {
-        lyd_free_all(*tree);
-        *tree = NULL;
+    if (lydctx) {
+        lydctx->free(lydctx);
     }
-    return ret;
+    if (rc) {
+        if (parent) {
+            /* free all the parsed subtrees */
+            for (i = 0; i < parsed.count; ++i) {
+                lyd_free_tree(parsed.dnodes[i]);
+            }
+        } else {
+            /* free everything */
+            lyd_free_all(*first_p);
+            *first_p = NULL;
+        }
+    }
+    ly_set_erase(&parsed, NULL);
+    return rc;
 }
 
 API LY_ERR
-lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options,
-        struct lyd_node **tree)
+lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+        uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, in, parent || tree, LY_EINVAL);
+    LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL);
+    LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL);
+
+    return lyd_parse(ctx, parent, tree, in, format, parse_options, validate_options, NULL);
+}
+
+API LY_ERR
+lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options,
+        uint32_t validate_options, struct lyd_node **tree)
 {
     LY_ERR ret;
     struct ly_in *in;
 
     LY_CHECK_RET(ly_in_new_memory(data, &in));
-    ret = lyd_parse_data(ctx, in, format, parse_options, validate_options, tree);
+    ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
 
     ly_in_free(in, 0);
     return ret;
@@ -404,7 +419,7 @@
     struct ly_in *in;
 
     LY_CHECK_RET(ly_in_new_fd(fd, &in));
-    ret = lyd_parse_data(ctx, in, format, parse_options, validate_options, tree);
+    ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
 
     ly_in_free(in, 0);
     return ret;
@@ -418,53 +433,23 @@
     struct ly_in *in;
 
     LY_CHECK_RET(ly_in_new_filepath(path, 0, &in));
-    ret = lyd_parse_data(ctx, in, format, parse_options, validate_options, tree);
+    ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
 
     ly_in_free(in, 0);
     return ret;
 }
 
 API LY_ERR
-lyd_parse_rpc(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree, struct lyd_node **op)
+lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+        enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op)
 {
-    LY_CHECK_ARG_RET(ctx, ctx, in, tree, LY_EINVAL);
+    LY_ERR rc = LY_SUCCESS;
+    struct lyd_ctx *lydctx = NULL;
+    struct ly_set parsed = {0};
+    struct lyd_node *first = NULL, *envp = NULL;
+    uint32_t i, parse_opts, val_opts;
 
-    format = lyd_parse_get_format(in, format);
-    LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
-
-    /* init */
-    *tree = NULL;
-    if (op) {
-        *op = NULL;
-    }
-
-    /* remember input position */
-    in->func_start = in->current;
-
-    switch (format) {
-    case LYD_XML:
-        return lyd_parse_xml_rpc(ctx, in, tree, op);
-    case LYD_JSON:
-        return lyd_parse_json_rpc(ctx, in, tree, op);
-    case LYD_LYB:
-        return lyd_parse_lyb_rpc(ctx, in, tree, op);
-    case LYD_UNKNOWN:
-        break;
-    }
-
-    LOGINT_RET(ctx);
-}
-
-API LY_ERR
-lyd_parse_reply(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree,
-        struct lyd_node **op)
-{
-    LY_CHECK_ARG_RET(ctx, ctx, in, tree || op, LY_EINVAL);
-
-    format = lyd_parse_get_format(in, format);
-    LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
-
-    /* init */
+    LY_CHECK_ARG_RET(ctx, ctx || parent, in, data_type, parent || tree || op, LY_EINVAL);
     if (tree) {
         *tree = NULL;
     }
@@ -472,54 +457,75 @@
         *op = NULL;
     }
 
-    /* remember input position */
-    in->func_start = in->current;
-
-    switch (format) {
-    case LYD_XML:
-        return lyd_parse_xml_reply(ctx, in, tree, op);
-    case LYD_JSON:
-        return lyd_parse_json_reply(ctx, in, tree, op);
-    case LYD_LYB:
-        return lyd_parse_lyb_reply(ctx, in, tree, op);
-    case LYD_UNKNOWN:
-        break;
-    }
-
-    LOGINT_RET(ctx);
-}
-
-API LY_ERR
-lyd_parse_notif(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree, struct lyd_node **ntf)
-{
-    LY_CHECK_ARG_RET(ctx, ctx, in, tree || ntf, LY_EINVAL);
-
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
 
-    /* init */
-    if (tree) {
-        *tree = NULL;
-    }
-    if (ntf) {
-        *ntf = NULL;
-    }
-
     /* remember input position */
     in->func_start = in->current;
 
+    /* check params based on the data type */
+    if (data_type == LYD_TYPE_NETCONF_RPC) {
+        LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL);
+    } else if (data_type == LYD_TYPE_NETCONF_REPLY_OR_NOTIF) {
+        LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, op, LY_EINVAL);
+    }
+    parse_opts = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
+    val_opts = 0;
+
+    /* parse the data */
     switch (format) {
     case LYD_XML:
-        return lyd_parse_xml_notif(ctx, in, tree, ntf);
+        rc = lyd_parse_xml(ctx, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+        break;
     case LYD_JSON:
-        return lyd_parse_json_notif(ctx, in, tree, ntf);
+        rc = lyd_parse_json(ctx, parent, &first, in, parse_opts, val_opts, data_type, &parsed, &lydctx);
+        break;
     case LYD_LYB:
-        return lyd_parse_lyb_notif(ctx, in, tree, ntf);
+        rc = lyd_parse_lyb(ctx, parent, &first, in, parse_opts, val_opts, data_type, &parsed, &lydctx);
+        break;
     case LYD_UNKNOWN:
+        LOGINT(ctx);
+        rc = LY_EINT;
         break;
     }
+    LY_CHECK_GOTO(rc, cleanup);
 
-    LOGINT_RET(ctx);
+    /* set out params correctly */
+    if (tree) {
+        if (envp) {
+            /* special out param meaning */
+            *tree = envp;
+        } else {
+            *tree = parent ? NULL : first;
+        }
+    }
+    if (op) {
+        *op = lydctx->op_node;
+    }
+
+cleanup:
+    if (lydctx) {
+        lydctx->free(lydctx);
+    }
+    if (rc) {
+        if (parent) {
+            /* free all the parsed subtrees */
+            for (i = 0; i < parsed.count; ++i) {
+                lyd_free_tree(parsed.dnodes[i]);
+            }
+        } else {
+            /* free everything (cannot occur in the current code, a safety) */
+            lyd_free_all(first);
+            if (tree) {
+                *tree = NULL;
+            }
+            if (op) {
+                *op = NULL;
+            }
+        }
+    }
+    ly_set_erase(&parsed, NULL);
+    return rc;
 }
 
 LY_ERR
@@ -1586,7 +1592,7 @@
                 }
 
                 /* create any default children */
-                LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, node_types, node_when,
+                LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, node_types, node_when,
                         impl_opts, diff));
             }
             break;
@@ -1670,7 +1676,7 @@
         /* 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_children_p(node), NULL, NULL, NULL,
+            LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL,
                     &node_when, implicit_options, diff), cleanup);
         }
 
@@ -2995,7 +3001,7 @@
         } else {
             /* check descendants, recursively */
             LY_LIST_FOR_SAFE(lyd_child_no_keys(sibling_src), tmp, child_src) {
-                LY_CHECK_RET(lyd_merge_sibling_r(lyd_node_children_p(match_trg), match_trg, &child_src, options));
+                LY_CHECK_RET(lyd_merge_sibling_r(lyd_node_child_p(match_trg), match_trg, &child_src, options));
             }
         }
     } else {