data tree FEATURE support for NETCONF messages
diff --git a/src/in.c b/src/in.c
index 8912b32..f4c8fd6 100644
--- a/src/in.c
+++ b/src/in.c
@@ -381,50 +381,108 @@
 }
 
 LY_ERR
+lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op)
+{
+    const struct lyd_node *iter;
+
+    *op = NULL;
+
+    if (!parent) {
+        /* no parent, nothing to look for */
+        return LY_SUCCESS;
+    }
+
+    /* we need to find the operation node if it already exists */
+    for (iter = parent; iter; iter = lyd_parent(iter)) {
+        if (iter->schema && (iter->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))) {
+            break;
+        }
+    }
+
+    if (!iter) {
+        /* no operation found */
+        return LY_SUCCESS;
+    }
+
+    if (!(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY))) {
+        LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent %s \"%s\" node when not parsing any operation.",
+                lys_nodetype2str(iter->schema->nodetype), iter->schema->name);
+        return LY_EINVAL;
+    } else if ((iter->schema->nodetype == LYS_RPC) && !(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY))) {
+        LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent RPC \"%s\" node when not parsing RPC nor rpc-reply.",
+                iter->schema->name);
+        return LY_EINVAL;
+    } else if ((iter->schema->nodetype == LYS_ACTION) && !(int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY))) {
+        LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent action \"%s\" node when not parsing action nor rpc-reply.",
+                iter->schema->name);
+        return LY_EINVAL;
+    } else if ((iter->schema->nodetype == LYS_NOTIF) && !(int_opts & LYD_INTOPT_NOTIF)) {
+        LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent notification \"%s\" node when not parsing a notification.",
+                iter->schema->name);
+        return LY_EINVAL;
+    }
+
+    *op = (struct lyd_node *)iter;
+    return LY_SUCCESS;
+}
+
+LY_ERR
 lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
 {
-    LY_ERR ret = LY_EVALID;
+    LY_ERR rc = LY_SUCCESS;
 
     LOG_LOCSET(snode, NULL, NULL, NULL);
 
-    if ((lydctx->parse_options & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
+    if ((lydctx->parse_opts & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
         LOGVAL(lydctx->data_ctx->ctx, LY_VCODE_INNODE, "state", snode->name);
+        rc = LY_EVALID;
         goto cleanup;
     }
 
-    if (snode->nodetype & (LYS_RPC | LYS_ACTION)) {
+    if (snode->nodetype == LYS_RPC) {
         if (lydctx->int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) {
             if (lydctx->op_node) {
-                LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
-                        lys_nodetype2str(snode->nodetype), snode->name,
-                        lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
-                goto cleanup;
+                goto error_node_dup;
             }
         } else {
-            LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\".",
-                    lys_nodetype2str(snode->nodetype), snode->name);
-            goto cleanup;
+            goto error_node_inval;
+        }
+    } else if (snode->nodetype == LYS_ACTION) {
+        if (lydctx->int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) {
+            if (lydctx->op_node) {
+                goto error_node_dup;
+            }
+        } else {
+            goto error_node_inval;
         }
     } else if (snode->nodetype == LYS_NOTIF) {
         if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
             if (lydctx->op_node) {
-                LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
-                        lys_nodetype2str(snode->nodetype), snode->name,
-                        lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
-                goto cleanup;
+                goto error_node_dup;
             }
         } else {
-            LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\".",
-                    lys_nodetype2str(snode->nodetype), snode->name);
-            goto cleanup;
+            goto error_node_inval;
         }
     }
 
-    ret = LY_SUCCESS;
+    /* success */
+    goto cleanup;
+
+error_node_dup:
+    LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+            lys_nodetype2str(snode->nodetype), snode->name, lys_nodetype2str(lydctx->op_node->schema->nodetype),
+            lydctx->op_node->schema->name);
+    rc = LY_EVALID;
+    goto cleanup;
+
+error_node_inval:
+    LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\".", lys_nodetype2str(snode->nodetype),
+            snode->name);
+    rc = LY_EVALID;
 
 cleanup:
     LOG_LOCBACK(1, 0, 0, 0);
-    return ret;
+    return rc;
 }
 
 LY_ERR
@@ -435,7 +493,7 @@
 
     LY_CHECK_RET(lyd_create_term(schema, value, value_len, dynamic, format, prefix_data, hints, &incomplete, node));
 
-    if (incomplete && !(lydctx->parse_options & LYD_PARSE_ONLY)) {
+    if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
         LY_CHECK_RET(ly_set_add(&lydctx->node_types, *node, 1, NULL));
     }
     return LY_SUCCESS;
@@ -457,7 +515,7 @@
     LY_CHECK_RET(lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, format, prefix_data,
             hints, 0, &incomplete));
 
-    if (incomplete && !(lydctx->parse_options & LYD_PARSE_ONLY)) {
+    if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
         LY_CHECK_RET(ly_set_add(&lydctx->meta_types, *meta, 1, NULL));
     }