path UPDTAE deref func support in leafref (#2030)
diff --git a/src/path.c b/src/path.c
index cfa934f..4bfb76d 100644
--- a/src/path.c
+++ b/src/path.c
@@ -264,6 +264,60 @@
return LY_EVALID;
}
+/**
+ * @brief Parse deref XPath function and perform all additional checks.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Optional context node, used for logging.
+ * @param[in] exp Parsed path.
+ * @param[in,out] tok_idx Index in @p exp, is adjusted.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_parse_deref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const struct lyxp_expr *exp,
+ uint32_t *tok_idx)
+{
+ size_t arg_len;
+ uint32_t begin_token, end_token;
+ struct lyxp_expr *arg_expr = NULL;
+
+ /* mandatory FunctionName */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_FUNCNAME), LY_EVALID);
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
+ LOGVAL(ctx, LYVE_XPATH, "Unexpected XPath function \"%.*s\" in path, expected \"deref(...)\"",
+ exp->tok_len[*tok_idx], exp->tok_pos[*tok_idx]);
+ return LY_EVALID;
+ }
+
+ /* mandatory '(' */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR1), LY_EVALID);
+ begin_token = *tok_idx;
+
+ /* count tokens till ')' */
+ while (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_PAR2) && *tok_idx < exp->used) {
+ /* emebedded functions are not allowed */
+ if (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_FUNCNAME)) {
+ LOGVAL(ctx, LYVE_XPATH, "Embedded function XPath function inside deref function within the path"
+ "is not allowed");
+ return LY_EVALID;
+ }
+
+ (*tok_idx)++;
+ }
+
+ /* mandatory ')' */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR2), LY_EVALID);
+ end_token = *tok_idx - 1;
+
+ /* parse the path of deref argument */
+ arg_len = exp->tok_pos[end_token] - exp->tok_pos[begin_token];
+ LY_CHECK_RET(ly_path_parse(ctx, ctx_node, &exp->expr[exp->tok_pos[begin_token]], arg_len, 1,
+ LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &arg_expr), LY_EVALID);
+ lyxp_expr_free(ctx, arg_expr);
+
+ return LY_SUCCESS;
+}
+
LY_ERR
ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len,
ly_bool lref, uint16_t begin, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr)
@@ -294,12 +348,23 @@
if (lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH)) {
/* relative path check specific to leafref */
if (lref) {
+ /* optional function 'deref..' */
+ if ((ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_EXTENDED) &&
+ !lyxp_check_token(NULL, exp, tok_idx, LYXP_TOKEN_FUNCNAME)) {
+ LY_CHECK_ERR_GOTO(ly_path_parse_deref(ctx, ctx_node, exp, &tok_idx), ret = LY_EVALID, error);
+
+ /* '/' */
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID,
+ error);
+ }
+
/* mandatory '..' */
LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_DDOT), ret = LY_EVALID, error);
do {
/* '/' */
- LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID,
+ error);
/* optional '..' */
} while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_DDOT));
@@ -866,6 +931,189 @@
}
/**
+ * @brief Duplicate ly_path_predicate structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] pred The array of path predicates.
+ * @param[out] dup Duplicated predicates.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_dup_predicates(const struct ly_ctx *ctx, const struct ly_path_predicate *pred, struct ly_path_predicate **dup)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!pred) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_CREATE_RET(ctx, *dup, LY_ARRAY_COUNT(pred), LY_EMEM);
+ LY_ARRAY_FOR(pred, u) {
+ LY_ARRAY_INCREMENT(*dup);
+ (*dup)[u].type = pred->type;
+
+ switch (pred->type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position-predicate */
+ (*dup)[u].position = pred->position;
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* key-predicate or leaf-list-predicate */
+ (*dup)[u].key = pred->key;
+ pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].value);
+ LY_ATOMIC_INC_BARRIER(((struct lysc_type *)pred->value.realtype)->refcount);
+ break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ /* key-predicate with a variable */
+ (*dup)[u].key = pred->key;
+ (*dup)[u].variable = strdup(pred->variable);
+ break;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Appends path elements from source to destination array
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] src The source path
+ * @param[in,out] dst The destination path
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_append(const struct ly_ctx *ctx, const struct ly_path *src, struct ly_path **dst)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_path *p;
+
+ if (!src) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_CREATE_RET(ctx, *dst, LY_ARRAY_COUNT(src), LY_EMEM);
+ LY_ARRAY_FOR(src, u) {
+ LY_ARRAY_NEW_GOTO(ctx, *dst, p, ret, error);
+ p->node = src[u].node;
+ p->ext = src[u].ext;
+ LY_CHECK_GOTO(ret = ly_path_dup_predicates(ctx, src[u].predicates, &p->predicates), error);
+ }
+
+ return LY_SUCCESS;
+
+error:
+ ly_path_free(ctx, *dst);
+ return (ret == LY_ENOTFOUND) ? LY_EVALID : ret;
+}
+
+/**
+ * @brief Compile deref XPath function into ly_path structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Optional context node, mandatory of @p lref.
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find
+ * the top-level node inside the extension instance instead of a module. Note that this is the case not only if
+ * the @p ctx_node is NULL, but also if the relative path starting in @p ctx_node reaches the document root
+ * via double dots.
+ * @param[in] expr Parsed path.
+ * @param[in] oper Oper option (@ref path_oper_options).
+ * @param[in] target Target option (@ref path_target_options).
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in,out] tok_idx Index in @p exp, is adjusted.
+ * @param[out] path Compiled path.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t *tok_idx, struct ly_path **path)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr expr2;
+ struct ly_path *path2 = NULL;
+ const struct lysc_node *node2;
+ const struct lysc_node_leaf *deref_leaf_node;
+ const struct lysc_type_leafref *lref;
+ uint32_t begin_token;
+
+ /* properly parsed path must always starts with 'deref' and '(' */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_FUNCNAME));
+ assert(!strncmp(&expr->expr[expr->tok_pos[*tok_idx]], "deref", 5));
+ (*tok_idx)++;
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR1));
+ (*tok_idx)++;
+ begin_token = *tok_idx;
+
+ /* emebedded functions were already identified count tokens till ')' */
+ while (lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2) && (*tok_idx < expr->used)) {
+ (*tok_idx)++;
+ }
+
+ /* properly parsed path must have ')' within the tokens */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
+
+ /* prepare expr representing just deref arg */
+ expr2.tokens = &expr->tokens[begin_token];
+ expr2.tok_pos = &expr->tok_pos[begin_token];
+ expr2.tok_len = &expr->tok_len[begin_token];
+ expr2.repeat = &expr->repeat[begin_token];
+ expr2.used = *tok_idx - begin_token;
+ expr2.size = expr->size - begin_token;
+ expr2.expr = expr->expr;
+
+ /* compile just deref arg, append it to the path and find dereferenced lref for next operations */
+ LY_CHECK_ERR_GOTO(ly_path_compile_leafref(ctx, ctx_node, top_ext, &expr2, oper, target, format, prefix_data,
+ &path2), ret = LY_EVALID, cleanup);
+ node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
+ deref_leaf_node = (const struct lysc_node_leaf *)node2;
+ lref = (const struct lysc_type_leafref *)deref_leaf_node->type;
+ LY_CHECK_ERR_GOTO(ly_path_append(ctx, path2, path), ret = LY_EVALID, cleanup);
+ ly_path_free(ctx, path2);
+ path2 = NULL;
+
+ /* compile dereferenced leafref expression and append it to the path */
+ LY_CHECK_ERR_GOTO(ly_path_compile_leafref(ctx, node2, top_ext, lref->path, oper, target, format, prefix_data,
+ &path2), ret = LY_EVALID, cleanup);
+ node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
+ LY_CHECK_ERR_GOTO(ly_path_append(ctx, path2, path), ret = LY_EVALID, cleanup);
+ ly_path_free(ctx, path2);
+ path2 = NULL;
+
+ /* properly parsed path must always continue with ')' and '/' */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
+ (*tok_idx)++;
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_OPER_PATH));
+ (*tok_idx)++;
+
+ /* prepare expr representing rest of the path after deref */
+ expr2.tokens = &expr->tokens[*tok_idx];
+ expr2.tok_pos = &expr->tok_pos[*tok_idx];
+ expr2.tok_len = &expr->tok_len[*tok_idx];
+ expr2.repeat = &expr->repeat[*tok_idx];
+ expr2.used = expr->used - *tok_idx;
+ expr2.size = expr->size - *tok_idx;
+ expr2.expr = expr->expr;
+
+ /* compile rest of the path and append it to the path */
+ LY_CHECK_ERR_GOTO(ly_path_compile_leafref(ctx, node2, top_ext, &expr2, oper, target, format, prefix_data, &path2),
+ ret = LY_EVALID, cleanup);
+ LY_CHECK_ERR_GOTO(ly_path_append(ctx, path2, path), ret = LY_EVALID, cleanup);
+
+cleanup:
+ ly_path_free(ctx, path2);
+ if (ret) {
+ ly_path_free(ctx, *path);
+ *path = NULL;
+ }
+ LOG_LOCBACK(1, 0, 0, 0);
+ return (ret == LY_ENOTFOUND) ? LY_EVALID : ret;
+}
+
+/**
* @brief Compile path into ly_path structure. Any predicates of a leafref are only checked, not compiled.
*
* @param[in] ctx libyang context.
@@ -922,7 +1170,13 @@
getnext_opts = 0;
}
- if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
+ if (lref && (ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_EXTENDED) &&
+ (expr->tokens[tok_idx] == LYXP_TOKEN_FUNCNAME)) {
+ /* deref function */
+ ret = ly_path_compile_deref(ctx, ctx_node, top_ext, expr, oper, target, format, prefix_data, &tok_idx, path);
+ ctx_node = (*path)[LY_ARRAY_COUNT(*path) - 1].node;
+ goto cleanup;
+ } else if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
/* absolute path */
ctx_node = NULL;
@@ -1158,7 +1412,8 @@
LY_ERR
ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup)
{
- LY_ARRAY_COUNT_TYPE u, v;
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
if (!path) {
return LY_SUCCESS;
@@ -1168,34 +1423,8 @@
LY_ARRAY_FOR(path, u) {
LY_ARRAY_INCREMENT(*dup);
(*dup)[u].node = path[u].node;
- if (path[u].predicates) {
- LY_ARRAY_CREATE_RET(ctx, (*dup)[u].predicates, LY_ARRAY_COUNT(path[u].predicates), LY_EMEM);
- LY_ARRAY_FOR(path[u].predicates, v) {
- struct ly_path_predicate *pred = &path[u].predicates[v];
-
- LY_ARRAY_INCREMENT((*dup)[u].predicates);
- (*dup)[u].predicates[v].type = pred->type;
-
- switch (pred->type) {
- case LY_PATH_PREDTYPE_POSITION:
- /* position-predicate */
- (*dup)[u].predicates[v].position = pred->position;
- break;
- case LY_PATH_PREDTYPE_LIST:
- case LY_PATH_PREDTYPE_LEAFLIST:
- /* key-predicate or leaf-list-predicate */
- (*dup)[u].predicates[v].key = pred->key;
- pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].predicates[v].value);
- LY_ATOMIC_INC_BARRIER(((struct lysc_type *)pred->value.realtype)->refcount);
- break;
- case LY_PATH_PREDTYPE_LIST_VAR:
- /* key-predicate with a variable */
- (*dup)[u].predicates[v].key = pred->key;
- (*dup)[u].predicates[v].variable = strdup(pred->variable);
- break;
- }
- }
- }
+ (*dup)[u].ext = path[u].ext;
+ LY_CHECK_RET(ret = ly_path_dup_predicates(ctx, path[u].predicates, &(*dup)[u].predicates), ret);
}
return LY_SUCCESS;