schema tree FEATURE functions for lysc node full dfs
diff --git a/src/plugins_exts_nacm.c b/src/plugins_exts_nacm.c
index 7012e66..8589bce 100644
--- a/src/plugins_exts_nacm.c
+++ b/src/plugins_exts_nacm.c
@@ -24,6 +24,49 @@
 LYEXT_VERSION_CHECK
  */
 
+struct nacm_dfs_arg {
+    struct lysc_ext_instance *c_ext;
+    struct lysc_node *parent;
+};
+
+/**
+ * @brief DFS callback implementation for inheriting the NACM extension.
+ */
+static LY_ERR
+nacm_inherit_clb(struct lysc_node *node, void *data, ly_bool *dfs_continue)
+{
+    struct nacm_dfs_arg *arg = data;
+    struct lysc_ext_instance *inherited;
+    LY_ARRAY_COUNT_TYPE u;
+
+    /* ignore the parent from which we inherit and input/output nodes */
+    if ((node != arg->parent) && !(node->nodetype & (LYS_INPUT | LYS_OUTPUT))) {
+        /* check that the node does not have its own NACM extension instance */
+        LY_ARRAY_FOR(node->exts, u) {
+            if (node->exts[u].def == arg->c_ext->def) {
+                /* the child already have its own NACM flag, so skip the subtree */
+                *dfs_continue = 1;
+                return LY_SUCCESS;
+            }
+        }
+
+        /* duplicate this one to inherit it to the child */
+        LY_ARRAY_NEW_RET(node->module->ctx, node->exts, inherited, LY_EMEM);
+
+        inherited->def = lysc_ext_dup(arg->c_ext->def);
+        inherited->parent = node;
+        inherited->parent_type = LYEXT_PAR_NODE;
+        if (arg->c_ext->argument) {
+            LY_CHECK_RET(lydict_insert(node->module->ctx, arg->c_ext->argument, strlen(arg->c_ext->argument),
+                    &inherited->argument));
+        }
+        /* TODO duplicate extension instances */
+        inherited->data = arg->c_ext->data;
+    }
+
+    return LY_SUCCESS;
+}
+
 /**
  * @brief Compile NAMC's extension instances.
  *
@@ -32,9 +75,9 @@
 LY_ERR
 nacm_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext)
 {
-    struct lysc_node *parent = NULL, *iter;
-    struct lysc_ext_instance *inherited;
+    struct lysc_node *parent = NULL;
     LY_ARRAY_COUNT_TYPE u;
+    struct nacm_dfs_arg dfs_arg;
 
     static const uint8_t nacm_deny_all = 1;
     static const uint8_t nacm_deny_write = 2;
@@ -85,33 +128,9 @@
     }
 
     /* inherit the extension instance to all the children nodes */
-    LYSC_TREE_DFS_BEGIN(parent, iter) {
-        /* ignore the parent from which we inherit and input/output nodes */
-        if ((iter != parent) && !(iter->nodetype & (LYS_INPUT | LYS_OUTPUT))) {
-            /* check that the node does not have its own NACM extension instance */
-            LY_ARRAY_FOR(iter->exts, u) {
-                if (iter->exts[u].def == c_ext->def) {
-                    /* the child already have its own NACM flag, so skip the subtree */
-                    LYSC_TREE_DFS_continue = 1;
-                    break;
-                }
-            }
-            if (!LYSC_TREE_DFS_continue) {
-                /* duplicate this one to inherit it to the child */
-                LY_ARRAY_NEW_RET(cctx->ctx, iter->exts, inherited, LY_EMEM);
-
-                inherited->def = lysc_ext_dup(c_ext->def);
-                inherited->parent = iter;
-                inherited->parent_type = LYEXT_PAR_NODE;
-                if (c_ext->argument) {
-                    LY_CHECK_RET(lydict_insert(cctx->ctx, c_ext->argument, strlen(c_ext->argument), &inherited->argument));
-                }
-                /* TODO duplicate extension instances */
-                inherited->data = c_ext->data;
-            }
-        }
-        LYSC_TREE_DFS_END(parent, iter)
-    }
+    dfs_arg.c_ext = c_ext;
+    dfs_arg.parent = parent;
+    LY_CHECK_RET(lysc_tree_dfs_full(parent, nacm_inherit_clb, &dfs_arg));
 
     return LY_SUCCESS;
 }
diff --git a/src/tree_schema.c b/src/tree_schema.c
index a898cb1..204fb51 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -42,6 +42,67 @@
 #include "tree_schema_internal.h"
 #include "xpath.h"
 
+API LY_ERR
+lysc_tree_dfs_full(const struct lysc_node *root, lysc_dfs_clb dfs_clb, void *data)
+{
+    struct lysc_node *elem, *ops, *elem2;
+    LY_ARRAY_COUNT_TYPE u;
+
+    LY_CHECK_ARG_RET(NULL, root, dfs_clb, LY_EINVAL);
+
+    LYSC_TREE_DFS_BEGIN(root, elem) {
+        /* schema node */
+        LY_CHECK_RET(dfs_clb(elem, data, &LYSC_TREE_DFS_continue));
+
+        ops = (struct lysc_node *)lysc_node_actions(elem);
+        LY_ARRAY_FOR(ops, u) {
+            LYSC_TREE_DFS_BEGIN(&ops[u], elem2) {
+                /* action subtree */
+                LY_CHECK_RET(dfs_clb(elem2, data, &LYSC_TREE_DFS_continue));
+
+                LYSC_TREE_DFS_END(&ops[u], elem2);
+            }
+        }
+
+        ops = (struct lysc_node *)lysc_node_notifs(elem);
+        LY_ARRAY_FOR(ops, u) {
+            LYSC_TREE_DFS_BEGIN(&ops[u], elem2) {
+                /* notification subtree */
+                LY_CHECK_RET(dfs_clb(elem2, data, &LYSC_TREE_DFS_continue));
+
+                LYSC_TREE_DFS_END(&ops[u], elem2);
+            }
+        }
+
+        LYSC_TREE_DFS_END(root, elem);
+    }
+
+    return LY_SUCCESS;
+}
+
+API LY_ERR
+lysc_module_dfs_full(const struct lys_module *mod, lysc_dfs_clb dfs_clb, void *data)
+{
+    LY_ARRAY_COUNT_TYPE u;
+
+    LY_CHECK_ARG_RET(NULL, mod, mod->compiled, dfs_clb, LY_EINVAL);
+
+    /* schema nodes */
+    LY_CHECK_RET(lysc_tree_dfs_full(mod->compiled->data, dfs_clb, data));
+
+    /* RPCs */
+    LY_ARRAY_FOR(mod->compiled->rpcs, u) {
+        LY_CHECK_RET(lysc_tree_dfs_full((struct lysc_node *)&mod->compiled->rpcs[u], dfs_clb, data));
+    }
+
+    /* notifications */
+    LY_ARRAY_FOR(mod->compiled->notifs, u) {
+        LY_CHECK_RET(lysc_tree_dfs_full((struct lysc_node *)&mod->compiled->notifs[u], dfs_clb, data));
+    }
+
+    return LY_SUCCESS;
+}
+
 API const struct lysc_node *
 lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, uint32_t options)
 {
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 8872ad3..f5b7697 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1882,6 +1882,48 @@
 const struct lysc_node *lysc_node_parent_full(const struct lysc_node *node);
 
 /**
+ * @brief Callback to be called for every schema node in a DFS traversal.
+ *
+ * @param[in] node Current node.
+ * @param[in] data Arbitrary user data.
+ * @param[out] dfs_continue Set to true if the current subtree should be skipped and continue with siblings instead.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value to terminate DFS and return this value.
+ */
+typedef LY_ERR (*lysc_dfs_clb)(struct lysc_node *node, void *data, ly_bool *dfs_continue);
+
+/**
+ * @brief DFS traversal of all the schema nodes in a (sub)tree including any actions and nested notifications.
+ *
+ * Node with children, actions, and notifications is traversed in this order:
+ * 1) each child subtree;
+ * 2) each action subtree;
+ * 3) each notification subtree.
+ *
+ * For algorithm illustration or traversal with actions and notifications skipped, see ::LYSC_TREE_DFS_BEGIN.
+ *
+ * @param[in] root Schema root to fully traverse.
+ * @param[in] dfs_clb Callback to call for each node.
+ * @param[in] data Arbitrary user data passed to @p dfs_clb.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value returned by @p dfs_clb.
+ */
+LY_ERR lysc_tree_dfs_full(const struct lysc_node *root, lysc_dfs_clb dfs_clb, void *data);
+
+/**
+ * @brief DFS traversal of all the schema nodes in a module including RPCs and notifications.
+ *
+ * For more details, see ::lysc_tree_dfs_full().
+ *
+ * @param[in] mod Module to fully traverse.
+ * @param[in] dfs_clb Callback to call for each node.
+ * @param[in] data Arbitrary user data passed to @p dfs_clb.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value returned by @p dfs_clb.
+ */
+LY_ERR lysc_module_dfs_full(const struct lys_module *mod, lysc_dfs_clb dfs_clb, void *data);
+
+/**
  * @brief Examine whether a node is user-ordered list or leaf-list.
  *
  * @param[in] schema Schema node to examine.