schema tree FEATURE lys_atomize_xpath API function

Tests included.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index f5a70b7..f0c386e 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -30,6 +30,7 @@
 #include "dict.h"
 #include "log.h"
 #include "set.h"
+#include "xpath.h"
 #include "tree.h"
 #include "tree_schema.h"
 #include "tree_schema_internal.h"
@@ -284,6 +285,50 @@
     return NULL;
 }
 
+API LY_ERR
+lys_atomize_xpath(const struct lysc_node *ctx_node, const char *xpath, int options, struct ly_set **set)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyxp_set xp_set;
+    struct lyxp_expr *exp;
+    uint32_t i;
+
+    LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
+    if (!(options & LYXP_SCNODE_ALL)) {
+        options = LYXP_SCNODE;
+    }
+
+    memset(&xp_set, 0, sizeof xp_set);
+
+    /* compile expression */
+    exp = lyxp_expr_parse(ctx_node->module->ctx, xpath);
+    LY_CHECK_ERR_GOTO(!exp, ret = LY_EINVAL, cleanup);
+
+    /* atomize expression */
+    ret = lyxp_atomize(exp, LYD_JSON, ctx_node->module, ctx_node, LYXP_NODE_ELEM, &xp_set, options);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* allocate return set */
+    *set = ly_set_new();
+    LY_CHECK_ERR_GOTO(!*set, LOGMEM(ctx_node->module->ctx); ret = LY_EMEM, cleanup);
+
+    /* transform into ly_set */
+    (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
+    LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx_node->module->ctx); ret = LY_EMEM, cleanup);
+    (*set)->size = xp_set.used;
+
+    for (i = 0; i < xp_set.used; ++i) {
+        if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) {
+            ly_set_add(*set, xp_set.val.scnodes[i].scnode, LY_SET_OPT_USEASLIST);
+        }
+    }
+
+cleanup:
+    lyxp_set_free_content(&xp_set);
+    lyxp_expr_free(ctx_node->module->ctx, exp);
+    return ret;
+}
+
 char *
 lysc_path_until(const struct lysc_node *node, const struct lysc_node *parent, LYSC_PATH_TYPE pathtype, char *buffer,
                 size_t buflen)
diff --git a/src/tree_schema.h b/src/tree_schema.h
index b3139dc..7764c64 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1731,14 +1731,30 @@
  * @brief Get the current status of the provided feature.
  *
  * @param[in] feature Compiled feature statement to examine.
- * @return
- * - 1 if feature is enabled,
- * - 0 if feature is disabled,
- * - -1 in case of error (invalid argument)
+ * @return 1 if feature is enabled,
+ * @return 0 if feature is disabled,
+ * @return -1 in case of error (invalid argument)
  */
 int lysc_feature_value(const struct lysc_feature *feature);
 
 /**
+ * @brief Get all the schema nodes (atoms) that are required for \p xpath to be evaluated.
+ *
+ * @param[in] ctx_node XPath schema context node.
+ * @param[in] xpath Data XPath expression filtering the matching nodes. ::LYD_JSON format is expected.
+ * @param[in] options Whether to apply some node access restrictions, one of the options should always be used.
+ * If none is set, ::LYXP_SCNODE is used.
+ * @param[out] set Set of found atoms (schema nodes).
+ * @return LY_SUCCESS on success, @p set is returned.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lys_atomize_xpath(const struct lysc_node *ctx_node, const char *xpath, int options, struct ly_set **set);
+
+#define LYXP_SCNODE 0x02        /**< No special tree access modifiers. */
+#define LYXP_SCNODE_SCHEMA 0x04 /**< Apply node access restrictions defined for 'when' and 'must' evaluation. */
+#define LYXP_SCNODE_OUTPUT 0x08 /**< Search RPC/action output nodes instead of input ones. */
+
+/**
  * @brief Types of the different schema paths.
  */
 typedef enum {
diff --git a/src/xpath.c b/src/xpath.c
index 4bbf305..5eb0998 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -5455,7 +5455,7 @@
 {
     int i, orig_used, idx, temp_ctx = 0, getnext_opts;
     uint32_t mod_idx;
-    const struct lysc_node *sub, *start_parent;
+    const struct lysc_node *iter, *start_parent;
 
     if (!set) {
         return LY_SUCCESS;
@@ -5492,11 +5492,11 @@
              * so use it directly (root node itself is useless in this case) */
             mod_idx = 0;
             while (mod || (mod = (struct lys_module *)ly_ctx_get_module_iter(set->ctx, &mod_idx))) {
-                sub = NULL;
+                iter = NULL;
                 /* module may not be implemented */
-                while (mod->implemented && (sub = lys_getnext(sub, NULL, mod->compiled, getnext_opts))) {
-                    if (!moveto_scnode_check(sub, set->root_type, ncname, mod)) {
-                        idx = lyxp_set_scnode_insert_node(set, sub, LYXP_NODE_ELEM);
+                while (mod->implemented && (iter = lys_getnext(iter, NULL, mod->compiled, getnext_opts))) {
+                    if (!moveto_scnode_check(iter, set->root_type, ncname, mod)) {
+                        idx = lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM);
                         /* we need to prevent these nodes from being considered in this moveto */
                         if ((idx < orig_used) && (idx > i)) {
                             set->val.scnodes[idx].in_ctx = 2;
@@ -5513,12 +5513,11 @@
                 mod = NULL;
             }
 
-        /* skip nodes without children - leaves, leaflists, and anyxmls (ouput root will eval to true) */
-        } else if (!(start_parent->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) {
-            sub = NULL;
-            while ((sub = lys_getnext(sub, start_parent, NULL, getnext_opts))) {
-                if (!moveto_scnode_check(sub, set->root_type, ncname, (mod ? mod : set->local_mod))) {
-                    idx = lyxp_set_scnode_insert_node(set, sub, LYXP_NODE_ELEM);
+        } else if (set->val.scnodes[i].type == LYXP_NODE_ELEM) {
+            iter = NULL;
+            while ((iter = lys_getnext(iter, start_parent, NULL, getnext_opts))) {
+                if (!moveto_scnode_check(iter, set->root_type, ncname, (mod ? mod : set->local_mod))) {
+                    idx = lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM);
                     if ((idx < orig_used) && (idx > i)) {
                         set->val.scnodes[idx].in_ctx = 2;
                         temp_ctx = 1;
@@ -6041,8 +6040,10 @@
 static LY_ERR
 moveto_scnode_self(struct lyxp_set *set, int all_desc, int options)
 {
-    const struct lysc_node *sub;
-    uint32_t i;
+    int getnext_opts;
+    uint32_t i, mod_idx;
+    const struct lysc_node *iter, *start_parent;
+    const struct lys_module *mod;
 
     if (!set) {
         return LY_SUCCESS;
@@ -6058,39 +6059,53 @@
         return LY_SUCCESS;
     }
 
-    /* add all the children, they get added recursively */
+    /* getnext opts */
+    getnext_opts = LYS_GETNEXT_NOSTATECHECK;
+    if (options & LYXP_SCNODE_OUTPUT) {
+        getnext_opts |= LYS_GETNEXT_OUTPUT;
+    }
+
+    /* add all the children, recursively as they are being added into the same set */
     for (i = 0; i < set->used; ++i) {
         if (set->val.scnodes[i].in_ctx != 1) {
             if (set->val.scnodes[i].in_ctx != -2) {
                 continue;
             }
 
-            /* remember context node (it was traversed again so it changes to a normal node) */
-            set->val.scnodes[i].in_ctx = 1;
+            /* remember context node */
+            set->val.scnodes[i].in_ctx = -1;
+        } else {
+            set->val.scnodes[i].in_ctx = 0;
         }
 
-        /* add all the children */
-        if (set->val.scnodes[i].scnode->nodetype & (LYS_LIST | LYS_CONTAINER)) {
-            sub = NULL;
-            while ((sub = lys_getnext(sub, set->val.scnodes[i].scnode, NULL, LYS_GETNEXT_NOSTATECHECK))) {
-                /* RPC input/output check */
-                if (options & LYXP_SCNODE_OUTPUT) {
-                    if (sub->parent->nodetype == LYS_INPUT) {
-                        continue;
-                    }
-                } else {
-                    if (sub->parent->nodetype == LYS_OUTPUT) {
-                        continue;
-                    }
-                }
+        start_parent = set->val.scnodes[i].scnode;
 
+        if ((set->val.scnodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.scnodes[i].type == LYXP_NODE_ROOT)) {
+            /* it can actually be in any module, it's all <running> */
+            mod_idx = 0;
+            while ((mod = (struct lys_module *)ly_ctx_get_module_iter(set->ctx, &mod_idx))) {
+                iter = NULL;
+                /* module may not be implemented */
+                while (mod->implemented && (iter = lys_getnext(iter, NULL, mod->compiled, getnext_opts))) {
+                    /* context check */
+                    if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (iter->flags & LYS_CONFIG_R)) {
+                        continue;
+                    }
+
+                    lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM);
+                    /* throw away the insert index, we want to consider that node again, recursively */
+                }
+            }
+
+        } else if (set->val.scnodes[i].type == LYXP_NODE_ELEM) {
+            iter = NULL;
+            while ((iter = lys_getnext(iter, start_parent, NULL, getnext_opts))) {
                 /* context check */
-                if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (sub->flags & LYS_CONFIG_R)) {
+                if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (iter->flags & LYS_CONFIG_R)) {
                     continue;
                 }
 
-                lyxp_set_scnode_insert_node(set, sub, LYXP_NODE_ELEM);
-                /* throw away the insert index, we want to consider that node again, recursively */
+                lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM);
             }
         }
     }
diff --git a/src/xpath.h b/src/xpath.h
index 33dc688..57845c7 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -3,7 +3,7 @@
  * @author Michal Vasko <mvasko@cesnet.cz>
  * @brief YANG XPath evaluation functions header
  *
- * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -287,9 +287,6 @@
 /**
  * @brief Get all the partial XPath nodes (atoms) that are required for @p exp to be evaluated.
  *
- * If any LYXP_SCNODE* options is set, only fatal errors are printed, otherwise they are downgraded
- * to warnings.
- *
  * @param[in] exp Parsed XPath expression to be evaluated.
  * @param[in] format Format of the XPath expression (more specifcally, of any used prefixes).
  * @param[in] local_mod Local module relative to the @p exp.
@@ -305,11 +302,7 @@
 LY_ERR lyxp_atomize(struct lyxp_expr *exp, LYD_FORMAT format, const struct lys_module *local_mod, const struct lysc_node *ctx_scnode,
                     enum lyxp_node_type ctx_scnode_type, struct lyxp_set *set, int options);
 
-/* these are used only internally */
-#define LYXP_SCNODE 0x02        /**< No special data tree access modifiers. */
-#define LYXP_SCNODE_SCHEMA 0x04 /**< Apply schema node access restrictions defined for 'when' and 'must' evaluation. */
-#define LYXP_SCNODE_OUTPUT 0x08 /**< Search RPC/action output instead of input. */
-
+/* used only internally */
 #define LYXP_SCNODE_ALL 0x0E
 
 /**