tree data FEATURE generic XPath evaluation function

Refs #1980
diff --git a/src/tree_data.c b/src/tree_data.c
index 69ce8e1..6c2061d 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -2765,58 +2765,19 @@
 }
 
 LIBYANG_API_DEF LY_ERR
-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)
+lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, 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, ctx_node, xpath, set, LY_EINVAL);
 
-    LY_CHECK_ARG_RET(NULL, tree, xpath, format, set, LY_EINVAL);
+    return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set);
+}
 
-    *set = NULL;
+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);
 
-    /* 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, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set,
-            LYXP_IGNORE_WHEN);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    if (xp_set.type != LYXP_SET_NODE_SET) {
-        LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath);
-        ret = LY_EINVAL;
-        goto cleanup;
-    }
-
-    /* allocate return set */
-    ret = ly_set_new(set);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* transform into ly_set, allocate memory for all the elements once (even though not all items must be
-     * elements but most likely will be) */
-    (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
-    LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(LYD_CTX(tree)); 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) {
-            ret = ly_set_add(*set, xp_set.val.nodes[i].node, 1, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
-        }
-    }
-
-cleanup:
-    lyxp_set_free_content(&xp_set);
-    lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), exp);
-    if (ret) {
-        ly_set_free(*set, NULL);
-        *set = NULL;
-    }
-    return ret;
+    return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set);
 }
 
 LIBYANG_API_DEF LY_ERR
@@ -2829,51 +2790,20 @@
 }
 
 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)
+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_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
+    LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL);
 
-    return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set);
+    *set = NULL;
+
+    return lyd_eval_xpath4(ctx_node, tree, NULL, xpath, format, prefix_data, vars, NULL, set, NULL, NULL, NULL);
 }
 
 LIBYANG_API_DEF LY_ERR
-lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set)
+lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result)
 {
-    LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
-
-    return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set);
-}
-
-LIBYANG_API_DEF LY_ERR
-lyd_eval_xpath3(const struct lyd_node *ctx_node, const struct lys_module *cur_mod, const char *xpath,
-        LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result)
-{
-    LY_ERR ret = LY_SUCCESS;
-    struct lyxp_set xp_set = {0};
-    struct lyxp_expr *exp = NULL;
-
-    LY_CHECK_ARG_RET(NULL, ctx_node, xpath, result, LY_EINVAL);
-
-    /* compile expression */
-    ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(ctx_node), xpath, 0, 1, &exp);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* evaluate expression */
-    ret = lyxp_eval(LYD_CTX(ctx_node), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, ctx_node, vars, &xp_set,
-            LYXP_IGNORE_WHEN);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* transform into boolean */
-    ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* set result */
-    *result = xp_set.val.bln;
-
-cleanup:
-    lyxp_set_free_content(&xp_set);
-    lyxp_expr_free((struct ly_ctx *)LYD_CTX(ctx_node), exp);
-    return ret;
+    return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result);
 }
 
 LIBYANG_API_DEF LY_ERR
@@ -2883,9 +2813,107 @@
 }
 
 LIBYANG_API_DEF LY_ERR
-lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result)
+lyd_eval_xpath3(const struct lyd_node *ctx_node, const struct lys_module *cur_mod, const char *xpath,
+        LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result)
 {
-    return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result);
+    return lyd_eval_xpath4(ctx_node, ctx_node, cur_mod, xpath, format, prefix_data, vars, NULL, NULL, NULL, NULL, result);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_eval_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const struct lys_module *cur_mod,
+        const char *xpath, LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, LY_XPATH_TYPE *ret_type,
+        struct ly_set **node_set, char **string, long double *number, ly_bool *boolean)
+{
+    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, ((ret_type && node_set && string && number && boolean) ||
+            (node_set && !string && !number && !boolean) || (!node_set && string && !number && !boolean) ||
+            (!node_set && !string && number && !boolean) || (!node_set && !string && !number && boolean)), LY_EINVAL);
+
+    /* 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, cur_mod, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set,
+            LYXP_IGNORE_WHEN);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* return expected result type without or with casting */
+    if (node_set) {
+        /* node set */
+        if (xp_set.type == LYXP_SET_NODE_SET) {
+            /* transform into a set */
+            LY_CHECK_GOTO(ret = ly_set_new(node_set), cleanup);
+            (*node_set)->objs = malloc(xp_set.used * sizeof *(*node_set)->objs);
+            LY_CHECK_ERR_GOTO(!(*node_set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup);
+            (*node_set)->size = xp_set.used;
+            for (i = 0; i < xp_set.used; ++i) {
+                if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) {
+                    ret = ly_set_add(*node_set, xp_set.val.nodes[i].node, 1, NULL);
+                    LY_CHECK_GOTO(ret, cleanup);
+                }
+            }
+            if (ret_type) {
+                *ret_type = LY_XPATH_NODE_SET;
+            }
+        } else if (!string && !number && !boolean) {
+            LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath);
+            ret = LY_EINVAL;
+            goto cleanup;
+        }
+    }
+
+    if (string) {
+        if ((xp_set.type != LYXP_SET_STRING) && !node_set) {
+            /* cast into string */
+            LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_STRING), cleanup);
+        }
+        if (xp_set.type == LYXP_SET_STRING) {
+            /* string */
+            *string = xp_set.val.str;
+            xp_set.val.str = NULL;
+            if (ret_type) {
+                *ret_type = LY_XPATH_STRING;
+            }
+        }
+    }
+
+    if (number) {
+        if ((xp_set.type != LYXP_SET_NUMBER) && !node_set) {
+            /* cast into number */
+            LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_NUMBER), cleanup);
+        }
+        if (xp_set.type == LYXP_SET_NUMBER) {
+            /* number */
+            *number = xp_set.val.num;
+            if (ret_type) {
+                *ret_type = LY_XPATH_NUMBER;
+            }
+        }
+    }
+
+    if (boolean) {
+        if ((xp_set.type != LYXP_SET_BOOLEAN) && !node_set) {
+            /* cast into boolean */
+            LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN), cleanup);
+        }
+        if (xp_set.type == LYXP_SET_BOOLEAN) {
+            /* boolean */
+            *boolean = xp_set.val.bln;
+            if (ret_type) {
+                *ret_type = LY_XPATH_BOOLEAN;
+            }
+        }
+    }
+
+cleanup:
+    lyxp_set_free_content(&xp_set);
+    lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), exp);
+    return ret;
 }
 
 LIBYANG_API_DEF LY_ERR