blob: bcbfe01f7ebcd37a96db778634978ae69e94fb3a [file] [log] [blame]
Michal Vasko004d3152020-06-11 19:59:22 +02001/**
2 * @file path.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief Path functions
5 *
6 * Copyright (c) 2020 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14#define _ISOC99_SOURCE /* strtoull */
15
16#include "path.h"
17
18#include <assert.h>
19#include <ctype.h>
20#include <stdlib.h>
21
22#include "common.h"
23#include "log.h"
24#include "plugins_types.h"
25#include "tree_data_internal.h"
26#include "tree_schema_internal.h"
27#include "xpath.h"
28
29/**
30 * @brief Check predicate syntax.
31 *
32 * @param[in] ctx libyang context.
33 * @param[in] exp Parsed predicate.
34 * @param[in,out] tok_idx Index in @p exp, is adjusted.
35 * @param[in] prefix Prefix option.
36 * @param[in] pred Predicate option.
37 * @return LY_ERR value.
38 */
39static LY_ERR
40ly_path_check_predicate(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint16_t *tok_idx, uint8_t prefix,
41 uint8_t pred)
42{
43 struct ly_set *set = NULL;
44 uint32_t i;
45 const char *name;
46 size_t name_len;
47
48 /* '[' */
49 if (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1)) {
50 if (((pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_KEYS))
51 && !lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NAMETEST)) {
52 set = ly_set_new();
53 LY_CHECK_ERR_GOTO(!set, LOGMEM(ctx), error);
54
55 do {
56 /* NameTest is always expected here */
57 LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NAMETEST), error);
58
59 /* check prefix based on the options */
60 name = strnstr(exp->expr + exp->tok_pos[*tok_idx], ":", exp->tok_len[*tok_idx]);
61 if ((prefix == LY_PATH_PREFIX_MANDATORY) && !name) {
62 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", exp->tok_len[*tok_idx],
63 exp->expr + exp->tok_pos[*tok_idx]);
64 goto error;
65 }
66 if (!name) {
67 name = exp->expr + exp->tok_pos[*tok_idx];
68 name_len = exp->tok_len[*tok_idx];
69 } else {
70 ++name;
71 name_len = exp->tok_len[*tok_idx] - (name - (exp->expr + exp->tok_pos[*tok_idx]));
72 }
73
74 /* check whether it was not already specified */
75 for (i = 0; i < set->count; ++i) {
76 /* all the keys must be from the same module so this comparison should be fine */
77 if (!strncmp(set->objs[i], name, name_len) && !isalpha(((char *)set->objs[i])[name_len])) {
78 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Duplicate predicate key \"%.*s\" in path.",
79 name_len, name);
80 goto error;
81 }
82 }
83
84 /* add it into the set */
85 ly_set_add(set, (void *)name, LY_SET_OPT_USEASLIST);
86
87 /* NameTest */
88 ++(*tok_idx);
89
90 /* '=' */
91 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), error);
92
93 /* Literal */
94 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL), error);
95
96 /* ']' */
97 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), error);
98
99 /* '[' */
100 } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1));
101
102 /* '.' */
103 } else if ((pred == LY_PATH_PRED_SIMPLE) && !lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_DOT)) {
104 /* '=' */
105 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), error);
106
107 /* Literal */
108 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL), error);
109
110 /* ']' */
111 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), error);
112
113 /* Number */
114 } else if ((pred == LY_PATH_PRED_SIMPLE) && !lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_NUMBER)) {
115
116 /* ']' */
117 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), error);
118
119 } else if ((pred == LY_PATH_PRED_LEAFREF) && !lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NAMETEST)) {
120 assert(prefix == LY_PATH_PREFIX_OPTIONAL);
121 set = ly_set_new();
122 LY_CHECK_ERR_GOTO(!set, LOGMEM(ctx), error);
123
124 do {
125 /* NameTest is always expected here */
126 LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NAMETEST), error);
127
128 name = strnstr(exp->expr + exp->tok_pos[*tok_idx], ":", exp->tok_len[*tok_idx]);
129 if (!name) {
130 name = exp->expr + exp->tok_pos[*tok_idx];
131 name_len = exp->tok_len[*tok_idx];
132 } else {
133 ++name;
134 name_len = exp->tok_len[*tok_idx] - (name - (exp->expr + exp->tok_pos[*tok_idx]));
135 }
136
137 /* check whether it was not already specified */
138 for (i = 0; i < set->count; ++i) {
139 /* all the keys must be from the same module so this comparison should be fine */
140 if (!strncmp(set->objs[i], name, name_len) && !isalpha(((char *)set->objs[i])[name_len])) {
141 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Duplicate predicate key \"%.*s\" in path.",
142 name_len, name);
143 goto error;
144 }
145 }
146
147 /* add it into the set */
148 ly_set_add(set, (void *)name, LY_SET_OPT_USEASLIST);
149
150 /* NameTest */
151 ++(*tok_idx);
152
153 /* '=' */
154 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), error);
155
156 /* FuncName */
157 LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME), error);
158 if ((exp->tok_len[*tok_idx] != 7) || strncmp(exp->expr + exp->tok_pos[*tok_idx], "current", 7)) {
159 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Invalid function \"%.*s\" invocation in path.",
160 exp->tok_len[*tok_idx], exp->expr + exp->tok_pos[*tok_idx]);
161 goto error;
162 }
163 ++(*tok_idx);
164
165 /* '(' */
166 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR1), error);
167
168 /* ')' */
169 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR2), error);
170
171 /* '/' */
172 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_PATH), error);
173
174 /* '..' */
175 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_DDOT), error);
176 do {
177 /* '/' */
178 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_PATH), error);
179 } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_DDOT));
180
181 /* NameTest */
182 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), error);
183
184 /* '/' */
185 while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_OPER_PATH)) {
186 /* NameTest */
187 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), error);
188 }
189
190 /* ']' */
191 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), error);
192
193 /* '[' */
194 } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1));
195
196 } else {
197 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK, lyxp_print_token(exp->tokens[*tok_idx]),
198 exp->expr + exp->tok_pos[*tok_idx]);
199 goto error;
200 }
201 }
202
203 ly_set_free(set, NULL);
204 return LY_SUCCESS;
205
206error:
207 ly_set_free(set, NULL);
208 return LY_EVALID;
209}
210
211LY_ERR
212ly_path_parse(const struct ly_ctx *ctx, const char *str_path, size_t path_len, uint8_t begin, uint8_t lref,
213 uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
214{
215 struct lyxp_expr *exp;
216 uint16_t tok_idx;
217
218 assert((begin == LY_PATH_BEGIN_ABSOLUTE) || (begin == LY_PATH_BEGIN_EITHER));
219 assert((lref == LY_PATH_LREF_TRUE) || (lref == LY_PATH_LREF_FALSE));
220 assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY));
221 assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
222
223 /* parse as a generic XPath expression */
224 exp = lyxp_expr_parse(ctx, str_path, path_len, 1);
225 LY_CHECK_GOTO(!exp, error);
226 tok_idx = 0;
227
228 if (begin == LY_PATH_BEGIN_EITHER) {
229 /* is the path relative? */
230 if (lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH)) {
231 /* '..' */
232 while ((lref == LY_PATH_LREF_TRUE) && !lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_DDOT)) {
233 /* '/' */
234 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), error);
235 }
236 }
237 } else {
238 /* '/' */
239 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), error);
240 }
241
242 do {
243 /* NameTest */
244 LY_CHECK_GOTO(lyxp_check_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), error);
245
246 /* check prefix based on the options */
247 if ((prefix == LY_PATH_PREFIX_MANDATORY) && !strnstr(exp->expr + exp->tok_pos[tok_idx], ":", exp->tok_len[tok_idx])) {
248 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", exp->tok_len[tok_idx],
249 exp->expr + exp->tok_pos[tok_idx]);
250 goto error;
251 }
252
253 ++tok_idx;
254
255 /* Predicate* */
256 LY_CHECK_GOTO(ly_path_check_predicate(ctx, exp, &tok_idx, prefix, pred), error);
257
258 /* '/' */
259 } while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH));
260
261 /* trailing token check */
262 if (exp->used > tok_idx) {
263 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of path.",
264 exp->expr + exp->tok_pos[tok_idx]);
265 goto error;
266 }
267
268 *expr = exp;
269 return LY_SUCCESS;
270
271error:
272 lyxp_expr_free(ctx, exp);
273 return LY_EINVAL;
274}
275
276LY_ERR
277ly_path_parse_predicate(const struct ly_ctx *ctx, const char *str_path, size_t path_len, uint8_t prefix, uint8_t pred,
278 struct lyxp_expr **expr)
279{
280 struct lyxp_expr *exp;
281 uint16_t tok_idx;
282
283 assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY));
284 assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
285
286 /* parse as a generic XPath expression */
287 exp = lyxp_expr_parse(ctx, str_path, path_len, 0);
288 LY_CHECK_GOTO(!exp, error);
289 tok_idx = 0;
290
291 LY_CHECK_GOTO(ly_path_check_predicate(ctx, exp, &tok_idx, prefix, pred), error);
292
293 /* trailing token check */
294 if (exp->used > tok_idx) {
295 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of predicate.",
296 exp->expr + exp->tok_pos[tok_idx]);
297 goto error;
298 }
299
300 *expr = exp;
301 return LY_SUCCESS;
302
303error:
304 lyxp_expr_free(ctx, exp);
305 return LY_EINVAL;
306}
307
308/**
309 * @brief Parse prefix from a NameTest, if any, and node name, and return expected module of the node.
310 *
Michal Vasko00cbf532020-06-15 13:58:47 +0200311 * @param[in] ctx libyang context.
Michal Vasko004d3152020-06-11 19:59:22 +0200312 * @param[in] cur_mod Module of the current (original context) node. Needed for ::LYD_SCHEMA.
313 * @param[in] prev_ctx_node Previous context node. Needed for ::LYD_JSON.
314 * @param[in] expr Parsed path.
315 * @param[in] tok_idx Index in @p expr.
316 * @param[in] lref Lref option.
317 * @param[in] resolve_prefix Callback for prefix resolution.
318 * @param[in] prefix_data Data for @p resolve_prefix.
319 * @param[in] format Format of the path.
320 * @param[out] mod Resolved module.
321 * @param[out] name Parsed node name.
322 * @param[out] name_len Length of @p name.
323 * @return LY_ERR value.
324 */
325static LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200326ly_path_compile_prefix(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *prev_ctx_node,
327 const struct lyxp_expr *expr, uint16_t tok_idx, uint8_t lref, ly_clb_resolve_prefix resolve_prefix,
328 void *prefix_data, LYD_FORMAT format, const struct lys_module **mod, const char **name, size_t *name_len)
Michal Vasko004d3152020-06-11 19:59:22 +0200329{
330 const char *ptr;
331 size_t len;
332
333 assert(expr->tokens[tok_idx] == LYXP_TOKEN_NAMETEST);
334
335 /* get prefix */
336 ptr = strnstr(expr->expr + expr->tok_pos[tok_idx], ":", expr->tok_len[tok_idx]);
337 len = ptr ? ptr - (expr->expr + expr->tok_pos[tok_idx]) : 0;
338
339 /* find next node module */
340 if (ptr) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200341 *mod = resolve_prefix(ctx, expr->expr + expr->tok_pos[tok_idx], len, prefix_data);
Michal Vasko004d3152020-06-11 19:59:22 +0200342 if (!*mod) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200343 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Prefix \"%.*s\" not found of a module in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200344 len, expr->expr + expr->tok_pos[tok_idx]);
345 return LY_EINVAL;
346 } else if (!(*mod)->implemented) {
347 if (lref == LY_PATH_LREF_FALSE) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200348 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
Michal Vasko004d3152020-06-11 19:59:22 +0200349 return LY_EINVAL;
350 }
351 lys_set_implemented_internal((struct lys_module *)*mod, 2);
352 }
353 } else {
354 switch (format) {
355 case LYD_SCHEMA:
356 *mod = cur_mod;
357 break;
358 case LYD_JSON:
359 if (!prev_ctx_node) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200360 LOGINT_RET(ctx);
Michal Vasko004d3152020-06-11 19:59:22 +0200361 }
362 *mod = prev_ctx_node->module;
363 break;
364 case LYD_XML:
365 /* not really defined */
Michal Vasko00cbf532020-06-15 13:58:47 +0200366 LOGINT_RET(ctx);
Michal Vasko004d3152020-06-11 19:59:22 +0200367 }
368 }
369
370 /* set name */
371 if (ptr) {
372 *name = ptr + 1;
373 *name_len = expr->tok_len[tok_idx] - len - 1;
374 } else {
375 *name = expr->expr + expr->tok_pos[tok_idx];
376 *name_len = expr->tok_len[tok_idx];
377 }
378
379 return LY_SUCCESS;
380}
381
382LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200383ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
384 const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
385 void *prefix_data, LYD_FORMAT format, struct ly_path_predicate **predicates,
386 enum ly_path_pred_type *pred_type)
Michal Vasko004d3152020-06-11 19:59:22 +0200387{
388 struct ly_path_predicate *p;
389 const struct lysc_node *key;
390 const struct lys_module *mod;
391 const char *name;
392 size_t name_len, key_count;
393
Michal Vasko00cbf532020-06-15 13:58:47 +0200394 assert(ctx && ctx_node);
395
Michal Vasko004d3152020-06-11 19:59:22 +0200396 *pred_type = 0;
397
398 /* '[' */
399 if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
400 /* no predicate */
401 return LY_SUCCESS;
402 }
403
404 if (expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST) {
405 if (ctx_node->nodetype != LYS_LIST) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200406 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200407 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
408 return LY_EINVAL;
409 } else if (ctx_node->flags & LYS_KEYLESS) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200410 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200411 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
412 return LY_EINVAL;
413 }
414
415 do {
416 /* NameTest, find the key */
Michal Vasko00cbf532020-06-15 13:58:47 +0200417 LY_CHECK_RET(ly_path_compile_prefix(ctx, cur_mod, ctx_node, expr, *tok_idx, LY_PATH_LREF_FALSE, resolve_prefix,
Michal Vasko004d3152020-06-11 19:59:22 +0200418 prefix_data, format, &mod, &name, &name_len));
419 key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
420 if (!key) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200421 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
Michal Vasko004d3152020-06-11 19:59:22 +0200422 return LY_EINVAL;
423 } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200424 LOGVAL(ctx, LY_VLOG_LYSC, key, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200425 lys_nodetype2str(key->nodetype), key->name);
426 return LY_EINVAL;
427 }
428 ++(*tok_idx);
429
430 /* new predicate */
431 if (!*pred_type) {
432 *pred_type = LY_PATH_PREDTYPE_LIST;
433 }
434 assert(*pred_type == LY_PATH_PREDTYPE_LIST);
Michal Vasko00cbf532020-06-15 13:58:47 +0200435 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200436 p->key = key;
437
438 /* '=' */
439 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
440 ++(*tok_idx);
441
442 /* Literal */
443 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL);
444 LY_CHECK_RET(lyd_value_store(&p->value, key, expr->expr + expr->tok_pos[*tok_idx] + 1,
445 expr->tok_len[*tok_idx] - 2, NULL, resolve_prefix, prefix_data, format));
446 ++(*tok_idx);
447
448 /* ']' */
449 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
450 ++(*tok_idx);
451
452 /* another predicate follows? */
453 } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1));
454
455 /* check that all keys were set */
456 key_count = 0;
457 for (key = lysc_node_children(ctx_node, 0); key && (key->flags & LYS_KEY); key = key->next) {
458 ++key_count;
459 }
460 if (LY_ARRAY_SIZE(*predicates) != key_count) {
461 /* names (keys) are unique - it was checked when parsing */
Michal Vasko00cbf532020-06-15 13:58:47 +0200462 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200463 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
Michal Vasko00cbf532020-06-15 13:58:47 +0200464 ly_path_predicates_free(ctx, LY_PATH_PREDTYPE_LIST, NULL, *predicates);
Michal Vasko004d3152020-06-11 19:59:22 +0200465 *predicates = NULL;
466 return LY_EINVAL;
467 }
468
469 } else if (expr->tokens[*tok_idx] == LYXP_TOKEN_DOT) {
470 if (ctx_node->nodetype != LYS_LEAFLIST) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200471 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Leaf-list predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200472 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
473 return LY_EINVAL;
474 }
475 ++(*tok_idx);
476
477 /* new predicate */
478 *pred_type = LY_PATH_PREDTYPE_LEAFLIST;
Michal Vasko00cbf532020-06-15 13:58:47 +0200479 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200480
481 /* '=' */
482 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
483 ++(*tok_idx);
484
485 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL);
486 /* store the value */
487 LY_CHECK_RET(lyd_value_store(&p->value, ctx_node, expr->expr + expr->tok_pos[*tok_idx] + 1,
488 expr->tok_len[*tok_idx] - 2, NULL, resolve_prefix, prefix_data, format));
489 ++(*tok_idx);
490
491 /* ']' */
492 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
493 ++(*tok_idx);
494 } else {
495 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER);
496 if (!(ctx_node->nodetype & (LYS_LEAFLIST | LYS_LIST))) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200497 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200498 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
499 return LY_EINVAL;
500 } else if (ctx_node->flags & LYS_CONFIG_W) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200501 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for configuration"
Michal Vasko004d3152020-06-11 19:59:22 +0200502 " %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
503 return LY_EINVAL;
504 }
Michal Vasko004d3152020-06-11 19:59:22 +0200505
506 /* new predicate */
507 *pred_type = LY_PATH_PREDTYPE_POSITION;
Michal Vasko00cbf532020-06-15 13:58:47 +0200508 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200509
510 /* syntax was already checked */
511 p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&name, 10);
Michal Vasko00cbf532020-06-15 13:58:47 +0200512 ++(*tok_idx);
Michal Vasko004d3152020-06-11 19:59:22 +0200513
514 /* ']' */
515 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
516 ++(*tok_idx);
517 }
518
519 return LY_SUCCESS;
520}
521
522/**
523 * @brief Compile leafref predicate. Actually, it is only checked.
524 *
525 * @param[in] ctx_node Context node, node for which the predicate is defined.
526 * @param[in] cur_node Current (original context) node.
527 * @param[in] expr Parsed path.
528 * @param[in,out] tok_idx Index in @p expr, is adjusted for parsed tokens.
529 * @param[in] resolve_prefix Callback for prefix resolution.
530 * @param[in] prefix_data Data for @p resolve_prefix.
531 * @param[in] format Format of the path.
532 * @return LY_ERR value.
533 */
534static LY_ERR
535ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct lysc_node *cur_node,
536 const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
537 void *prefix_data, LYD_FORMAT format)
538{
539 const struct lysc_node *key, *node, *node2;
540 const struct lys_module *mod;
541 const char *name;
542 size_t name_len;
543
544 /* '[' */
545 if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
546 /* no predicate */
547 return LY_SUCCESS;
548 }
549
550 if (ctx_node->nodetype != LYS_LIST) {
551 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
552 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
553 return LY_EINVAL;
554 } else if (ctx_node->flags & LYS_KEYLESS) {
555 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
556 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
557 return LY_EINVAL;
558 }
559
560 do {
561 /* NameTest, find the key */
Michal Vasko00cbf532020-06-15 13:58:47 +0200562 LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node->module, ctx_node, expr, *tok_idx,
563 LY_PATH_LREF_TRUE, resolve_prefix, prefix_data, format, &mod, &name, &name_len));
Michal Vasko004d3152020-06-11 19:59:22 +0200564 key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
565 if (!key) {
566 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
567 return LY_EINVAL;
568 } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
569 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
570 lys_nodetype2str(key->nodetype), key->name);
571 return LY_EINVAL;
572 }
573 ++(*tok_idx);
574
575 /* we are not actually compiling, throw the key away */
576 (void)key;
577
578 /* '=' */
579 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
580 ++(*tok_idx);
581
582 /* FuncName */
583 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_FUNCNAME);
584 ++(*tok_idx);
585
586 /* evaluating from the "current()" node */
587 node = cur_node;
588
589 /* '(' */
590 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
591 ++(*tok_idx);
592
593 /* ')' */
594 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
595 ++(*tok_idx);
596
597 do {
598 /* '/' */
599 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH);
600 ++(*tok_idx);
601
602 /* go to parent */
603 if (!node) {
604 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Too many parent references in path.");
605 return LY_EINVAL;
606 }
607 node = lysc_data_parent(node);
608
609 /* '..' */
610 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_DDOT);
611 ++(*tok_idx);
612 } while (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_DDOT);
613
614 do {
615 /* '/' */
616 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH);
617 ++(*tok_idx);
618
619 /* NameTest */
620 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST);
Michal Vasko00cbf532020-06-15 13:58:47 +0200621 LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node->module, node, expr, *tok_idx,
622 LY_PATH_LREF_TRUE, resolve_prefix, prefix_data, format, &mod, &name, &name_len));
Michal Vasko004d3152020-06-11 19:59:22 +0200623 node2 = lys_find_child(node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
624 if (!node2) {
625 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.",
626 name_len, name);
627 return LY_EINVAL;
628 }
629 node = node2;
630 ++(*tok_idx);
631 } while ((*tok_idx + 1 < expr->used) && (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_NAMETEST));
632
633 /* check the last target node */
634 if (node->nodetype != LYS_LEAF) {
635 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Leaf expected instead of %s \"%s\" in"
636 " leafref predicate in path.", lys_nodetype2str(node->nodetype), node->name);
637 return LY_EINVAL;
638 }
639
640 /* we are not actually compiling, throw the rightside node away */
641 (void)node;
642
643 /* ']' */
644 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
645 ++(*tok_idx);
646
647 /* another predicate follows? */
648 } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1));
649
650 return LY_SUCCESS;
651}
652
653LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200654ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
655 const struct lyxp_expr *expr, uint8_t lref, uint8_t oper, uint8_t target,
656 ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path)
Michal Vasko004d3152020-06-11 19:59:22 +0200657{
658 LY_ERR ret = LY_SUCCESS;
659 uint16_t tok_idx = 0;
660 const struct lys_module *mod;
661 const struct lysc_node *node2, *cur_node;
Michal Vasko00cbf532020-06-15 13:58:47 +0200662 struct ly_path *p = NULL;
663 int getnext_opts;
Michal Vasko004d3152020-06-11 19:59:22 +0200664 const char *name;
665 size_t name_len;
666
Michal Vasko00cbf532020-06-15 13:58:47 +0200667 assert(ctx);
668 assert((expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) || (lref == LY_PATH_LREF_FALSE) || ctx_node);
Michal Vasko004d3152020-06-11 19:59:22 +0200669 assert((lref == LY_PATH_LREF_TRUE) || (lref == LY_PATH_LREF_FALSE));
Michal Vasko00cbf532020-06-15 13:58:47 +0200670 assert((oper == LY_PATH_OPER_INPUT) || (oper == LY_PATH_OPER_OUTPUT));
671 assert((target == LY_PATH_TARGET_SINGLE) || (target == LY_PATH_TARGET_MANY));
Michal Vasko004d3152020-06-11 19:59:22 +0200672
673 if (lref == LY_PATH_LREF_TRUE) {
674 /* remember original context node */
675 cur_node = ctx_node;
676 }
677 *path = NULL;
678
Michal Vasko00cbf532020-06-15 13:58:47 +0200679 if (oper == LY_PATH_OPER_OUTPUT) {
680 getnext_opts = LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_OUTPUT;
681 } else {
682 getnext_opts = LYS_GETNEXT_NOSTATECHECK;
683 }
684
Michal Vasko004d3152020-06-11 19:59:22 +0200685 if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
686 /* absolute path */
687 ctx_node = NULL;
688
689 ++tok_idx;
690 } else {
691 /* relative path */
692 while ((lref == LY_PATH_LREF_TRUE) && (expr->tokens[tok_idx] == LYXP_TOKEN_DDOT)) {
693 if (!ctx_node) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200694 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Too many parent references in path.");
Michal Vasko004d3152020-06-11 19:59:22 +0200695 return LY_EINVAL;
696 }
697
698 /* get parent */
699 ctx_node = lysc_data_parent(ctx_node);
700
701 ++tok_idx;
702
703 assert(expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH);
704 ++tok_idx;
705 }
706
Michal Vasko00cbf532020-06-15 13:58:47 +0200707 /* we are not storing the parent */
708 (void)ctx_node;
Michal Vasko004d3152020-06-11 19:59:22 +0200709 }
710
711 do {
Michal Vasko00cbf532020-06-15 13:58:47 +0200712 /* check last compiled inner node, whether it is uniquely identified (even key-less list) */
713 if (p && (lref == LY_PATH_LREF_FALSE) && (p->node->nodetype == LYS_LIST) && !p->predicates) {
714 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
715 lys_nodetype2str(p->node->nodetype), p->node->name);
716 return LY_EINVAL;
717 }
718
Michal Vasko004d3152020-06-11 19:59:22 +0200719 /* get module and node name */
Michal Vasko00cbf532020-06-15 13:58:47 +0200720 LY_CHECK_GOTO(ret = ly_path_compile_prefix(ctx, cur_mod, ctx_node, expr, tok_idx, lref, resolve_prefix,
721 prefix_data, format, &mod, &name, &name_len), cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200722 ++tok_idx;
723
724 /* find the next node */
Michal Vasko00cbf532020-06-15 13:58:47 +0200725 node2 = lys_find_child(ctx_node, mod, name, name_len, 0, getnext_opts);
Michal Vasko004d3152020-06-11 19:59:22 +0200726 if (!node2) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200727 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
Michal Vasko004d3152020-06-11 19:59:22 +0200728 ret = LY_EINVAL;
729 goto cleanup;
730 }
731 ctx_node = node2;
732
733 /* new path segment */
Michal Vasko00cbf532020-06-15 13:58:47 +0200734 LY_ARRAY_NEW_GOTO(ctx, *path, p, ret, cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200735 p->node = ctx_node;
736
737 /* compile any predicates */
738 if (lref == LY_PATH_LREF_TRUE) {
739 ret = ly_path_compile_predicate_leafref(ctx_node, cur_node, expr, &tok_idx, resolve_prefix, prefix_data, format);
740 } else {
Michal Vasko00cbf532020-06-15 13:58:47 +0200741 ret = ly_path_compile_predicate(ctx, cur_mod, ctx_node, expr, &tok_idx, resolve_prefix, prefix_data, format,
Michal Vasko004d3152020-06-11 19:59:22 +0200742 &p->predicates, &p->pred_type);
743 }
744 LY_CHECK_GOTO(ret, cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200745 } while (!lyxp_next_token(NULL, expr, &tok_idx, LYXP_TOKEN_OPER_PATH));
746
Michal Vasko00cbf532020-06-15 13:58:47 +0200747 /* check last compiled node */
748 if ((lref == LY_PATH_LREF_FALSE) && (target == LY_PATH_TARGET_SINGLE)
749 && (p->node->nodetype & (LYS_LIST | LYS_LEAFLIST)) && !p->predicates) {
750 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
751 lys_nodetype2str(p->node->nodetype), p->node->name);
752 return LY_EINVAL;
753 }
754
Michal Vasko004d3152020-06-11 19:59:22 +0200755cleanup:
756 if (ret) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200757 ly_path_free(ctx, *path);
Michal Vasko004d3152020-06-11 19:59:22 +0200758 *path = NULL;
759 }
760 return ret;
761}
762
763LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200764ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_SIZE_TYPE *path_idx,
765 struct lyd_node **match)
Michal Vasko004d3152020-06-11 19:59:22 +0200766{
767 LY_ARRAY_SIZE_TYPE u;
Michal Vasko00cbf532020-06-15 13:58:47 +0200768 struct lyd_node *prev_node = NULL, *node, *target;
Michal Vasko004d3152020-06-11 19:59:22 +0200769 uint64_t pos;
770
771 assert(path && start);
772
773 if (lysc_data_parent(path[0].node)) {
774 /* relative path, start from the parent children */
775 start = lyd_node_children(start);
776 } else {
777 /* absolute path, start from the first top-level sibling */
778 while (start->parent) {
779 start = (struct lyd_node *)start->parent;
780 }
781 while (start->prev->next) {
782 start = start->prev;
783 }
784 }
785
786 LY_ARRAY_FOR(path, u) {
787 switch (path[u].pred_type) {
788 case LY_PATH_PREDTYPE_POSITION:
789 /* we cannot use hashes and want an instance on a specific position */
790 pos = 1;
791 node = (struct lyd_node *)start;
792 while (!lyd_find_sibling_next2(node, path[u].node, NULL, 0, &node)) {
793 if (pos == path[u].predicates[0].position) {
794 break;
795 }
796 ++pos;
797 }
798 break;
799 case LY_PATH_PREDTYPE_LEAFLIST:
800 /* we will use hashes to find one leaf-list instance */
801 LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target));
802 lyd_find_sibling_first(start, target, &node);
803 lyd_free_tree(target);
804 break;
805 case LY_PATH_PREDTYPE_LIST:
806 /* we will use hashes to find one list instance */
807 LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, &target));
808 lyd_find_sibling_first(start, target, &node);
809 lyd_free_tree(target);
810 break;
811 case LY_PATH_PREDTYPE_NONE:
812 /* we will use hashes to find one any/container/leaf instance */
813 lyd_find_sibling_val(start, path[u].node, NULL, 0, &node);
814 break;
815 }
816
817 if (!node) {
818 /* no matching nodes */
819 break;
820 }
821
Michal Vasko00cbf532020-06-15 13:58:47 +0200822 /* rememeber previous node */
823 prev_node = node;
824
Michal Vasko004d3152020-06-11 19:59:22 +0200825 /* next path segment, if any */
826 start = lyd_node_children(node);
827 }
828
Michal Vasko004d3152020-06-11 19:59:22 +0200829 if (node) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200830 /* we have found the full path */
831 if (path_idx) {
832 *path_idx = u;
833 }
834 if (match) {
835 *match = node;
836 }
Michal Vasko004d3152020-06-11 19:59:22 +0200837 return LY_SUCCESS;
Michal Vasko00cbf532020-06-15 13:58:47 +0200838
839 } else if (prev_node) {
840 /* we have found only some partial match */
841 if (path_idx) {
842 *path_idx = u - 1;
843 }
844 if (match) {
845 *match = prev_node;
846 }
847 return LY_EINCOMPLETE;
848 }
849
850 /* we have not found any nodes */
851 if (path_idx) {
852 *path_idx = 0;
853 }
854 if (match) {
855 *match = NULL;
856 }
857 return LY_ENOTFOUND;
858}
859
860LY_ERR
861ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match)
862{
863 LY_ERR ret;
864 struct lyd_node *m;
865
866 ret = ly_path_eval_partial(path, start, NULL, &m);
867
868 if (ret == LY_SUCCESS) {
869 /* last node was found */
870 if (match) {
871 *match = m;
872 }
873 return LY_SUCCESS;
874 }
875
876 /* not a full match */
877 if (match) {
878 *match = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +0200879 }
880 return LY_ENOTFOUND;
881}
882
883LY_ERR
884ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup)
885{
886 LY_ARRAY_SIZE_TYPE u, v;
887
888 if (!path) {
889 return LY_SUCCESS;
890 }
891
892 LY_ARRAY_CREATE_RET(ctx, *dup, LY_ARRAY_SIZE(path), LY_EMEM);
893 LY_ARRAY_FOR(path, u) {
894 LY_ARRAY_INCREMENT(*dup);
895 (*dup)[u].node = path[u].node;
896 if (path[u].predicates) {
897 LY_ARRAY_CREATE_RET(ctx, (*dup)[u].predicates, LY_ARRAY_SIZE(path[u].predicates), LY_EMEM);
898 (*dup)[u].pred_type = path[u].pred_type;
899 LY_ARRAY_FOR(path[u].predicates, v) {
900 struct ly_path_predicate *pred = &path[u].predicates[v];
901
902 LY_ARRAY_INCREMENT((*dup)[u].predicates);
903 switch (path[u].pred_type) {
904 case LY_PATH_PREDTYPE_POSITION:
905 /* position-predicate */
906 (*dup)[u].predicates[v].position = pred->position;
907 break;
908 case LY_PATH_PREDTYPE_LIST:
909 case LY_PATH_PREDTYPE_LEAFLIST:
910 /* key-predicate or leaf-list-predicate */
911 (*dup)[u].predicates[v].key = pred->key;
912 (*dup)[u].predicates[v].value.realtype = pred->value.realtype;
913 pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].predicates[v].value);
914 break;
915 case LY_PATH_PREDTYPE_NONE:
916 break;
917 }
918 }
919 }
920 }
921
922 return LY_SUCCESS;
923}
924
925void
926ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type, const struct lysc_node *llist,
927 struct ly_path_predicate *predicates)
928{
929 LY_ARRAY_SIZE_TYPE u;
930
931 if (!predicates) {
932 return;
933 }
934
935 LY_ARRAY_FOR(predicates, u) {
936 switch (pred_type) {
937 case LY_PATH_PREDTYPE_POSITION:
938 case LY_PATH_PREDTYPE_NONE:
939 /* nothing to free */
940 break;
941 case LY_PATH_PREDTYPE_LIST:
942 ((struct lysc_node_leaf *)predicates[u].key)->type->plugin->free(ctx, &predicates[u].value);
943 break;
944 case LY_PATH_PREDTYPE_LEAFLIST:
945 ((struct lysc_node_leaflist *)llist)->type->plugin->free(ctx, &predicates[u].value);
946 break;
947 }
948 }
949 LY_ARRAY_FREE(predicates);
950}
951
952void
953ly_path_free(const struct ly_ctx *ctx, struct ly_path *path)
954{
955 LY_ARRAY_SIZE_TYPE u;
956
957 LY_ARRAY_FOR(path, u) {
958 ly_path_predicates_free(ctx, path[u].pred_type, path[u].node, path[u].predicates);
959 }
960 LY_ARRAY_FREE(path);
961}