tree_data FEATURE add XPath variable bindings
diff --git a/src/tree_data.h b/src/tree_data.h
index 54a8575..eda8484 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -41,6 +41,7 @@
struct lyd_node_opaq;
struct lyd_node_term;
struct timespec;
+struct lyxp_var;
/**
* @page howtoData Data Instances
@@ -2315,6 +2316,26 @@
LY_ERR lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struct lyd_node **match);
/**
+ * @brief Set a new XPath variable to @p vars.
+ *
+ * @param[in,out] vars Pointer to [sized array](@ref sizedarrays) of XPath variables.
+ * To create a new array, set the @p vars target pointer to NULL.
+ * Otherwise variable named @p name with a value @p value will be added to the @p vars
+ * or its value will be changed if the variable is already defined.
+ * @param[in] name Name of the added/edited variable.
+ * @param[in] value Value of the variable.
+ * @return LY_ERR value.
+ */
+LY_ERR lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value);
+
+/**
+ * @brief Free the XPath variables.
+ *
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ */
+void lyxp_vars_free(struct lyxp_var *vars);
+
+/**
* @brief Search in the given data for instances of nodes matching the provided XPath.
*
* If a list instance is being selected with all its key values specified (but not necessarily ordered)
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index 6bf1a6d..3863ade 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -39,6 +39,7 @@
#include "tree_schema_internal.h"
#include "validation.h"
#include "xml.h"
+#include "xpath.h"
/**
* @brief Find an entry in duplicate instance cache for an instance. Create it if it does not exist.
@@ -163,6 +164,61 @@
}
}
+API LY_ERR
+lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *var_name = NULL, *var_value = NULL;
+ struct lyxp_var *item;
+
+ if (!vars || !name || !value) {
+ return LY_EINVAL;
+ }
+
+ /* If variable is already defined then change its value. */
+ if (*vars && !lyxp_vars_find(*vars, name, 0, &item)) {
+ var_value = strdup(value);
+ LY_CHECK_RET(!var_value, LY_EMEM);
+
+ /* Set new value. */
+ free(item->value);
+ item->value = var_value;
+ } else {
+ var_name = strdup(name);
+ var_value = strdup(value);
+ LY_CHECK_ERR_GOTO(!var_name || !var_value, ret = LY_EMEM, error);
+
+ /* Add new variable. */
+ LY_ARRAY_NEW_GOTO(NULL, *vars, item, ret, error);
+ item->name = var_name;
+ item->value = var_value;
+ }
+
+ return LY_SUCCESS;
+
+error:
+ free(var_name);
+ free(var_value);
+ return ret;
+}
+
+API void
+lyxp_vars_free(struct lyxp_var *vars)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!vars) {
+ return;
+ }
+
+ LY_ARRAY_FOR(vars, u) {
+ free(vars[u].name);
+ free(vars[u].value);
+ }
+
+ LY_ARRAY_FREE(vars);
+}
+
API struct lyd_node *
lyd_child_no_keys(const struct lyd_node *node)
{
diff --git a/src/xpath.c b/src/xpath.c
index 53835f5..e1eadc0 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -7733,6 +7733,30 @@
return LY_SUCCESS;
}
+LY_ERR
+lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var)
+{
+ LY_ERR ret = LY_ENOTFOUND;
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert(vars && name);
+
+ name_len = name_len ? name_len : strlen(name);
+
+ LY_ARRAY_FOR(vars, u) {
+ if (!strncmp(vars[u].name, name, name_len)) {
+ ret = LY_SUCCESS;
+ break;
+ }
+ }
+
+ if (var && !ret) {
+ *var = &vars[u];
+ }
+
+ return ret;
+}
+
/**
* @brief Evaluate PathExpr. Logs directly on error.
*
diff --git a/src/xpath.h b/src/xpath.h
index de0a7d3..eb832eb 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -222,6 +222,15 @@
} _PACKED;
/**
+ * @brief XPath variable bindings.
+ */
+struct lyxp_var {
+ char *name; /**< Variable name. In the XPath expression, the name is preceded by a '$' character. */
+ char *value; /**< The value of a variable is an object, which can be of any of the type that are possible
+ for the value of an expression. */
+};
+
+/**
* @brief XPath set - (partial) result.
*/
struct lyxp_set {
@@ -460,6 +469,17 @@
enum lyxp_token want_tok1, enum lyxp_token want_tok2);
/**
+ * @brief Find variable named @name in @p vars.
+ *
+ * @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
+ * @param[in] name Name of the variable being searched.
+ * @param[in] name_len Name length can be set to 0 if @p name is terminated by null byte.
+ * @param[out] var Variable that was found. The parameter is optional.
+ * @return LY_SUCCESS if the variable was found, otherwise LY_ENOTFOUND.
+ */
+LY_ERR lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var);
+
+/**
* @brief Frees a parsed XPath expression. @p expr should not be used afterwards.
*
* @param[in] ctx libyang context of the expression.
diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c
index 9a79a64..5f6fe7e 100644
--- a/tests/utests/data/test_tree_data.c
+++ b/tests/utests/data/test_tree_data.c
@@ -528,6 +528,56 @@
lyd_free_all(tree);
}
+static void
+test_lyxp_vars(void **UNUSED(state))
+{
+ struct lyxp_var *vars;
+
+ /* Test free. */
+ vars = NULL;
+ lyxp_vars_free(vars);
+
+ /* Bad arguments for lyxp_vars_add(). */
+ assert_int_equal(LY_EINVAL, lyxp_vars_set(NULL, "var1", "val1"));
+ assert_int_equal(LY_EINVAL, lyxp_vars_set(&vars, NULL, "val1"));
+ assert_int_equal(LY_EINVAL, lyxp_vars_set(&vars, "var1", NULL));
+ lyxp_vars_free(vars);
+ vars = NULL;
+
+ /* Add one item. */
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1"));
+ assert_int_equal(LY_ARRAY_COUNT(vars), 1);
+ assert_string_equal(vars[0].name, "var1");
+ assert_string_equal(vars[0].value, "val1");
+ lyxp_vars_free(vars);
+ vars = NULL;
+
+ /* Add three items. */
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "val2"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var3", "val3"));
+ assert_int_equal(LY_ARRAY_COUNT(vars), 3);
+ assert_string_equal(vars[0].name, "var1");
+ assert_string_equal(vars[0].value, "val1");
+ assert_string_equal(vars[1].name, "var2");
+ assert_string_equal(vars[1].value, "val2");
+ assert_string_equal(vars[2].name, "var3");
+ assert_string_equal(vars[2].value, "val3");
+ lyxp_vars_free(vars);
+ vars = NULL;
+
+ /* Change value of a variable. */
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "val2"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "new_value"));
+ assert_string_equal(vars[0].name, "var1");
+ assert_string_equal(vars[0].value, "new_value");
+ assert_string_equal(vars[1].name, "var2");
+ assert_string_equal(vars[1].value, "val2");
+ lyxp_vars_free(vars);
+ vars = NULL;
+}
+
int
main(void)
{
@@ -540,6 +590,7 @@
UTEST(test_first_sibling, setup),
UTEST(test_find_path, setup),
UTEST(test_data_hash, setup),
+ UTEST(test_lyxp_vars),
};
return cmocka_run_group_tests(tests, NULL, NULL);