blob: 47ef65746e7a80d006b441308f071c5f6b708853 [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"
Michal Vasko5aa44c02020-06-29 11:47:02 +020023#include "compat.h"
Michal Vasko004d3152020-06-11 19:59:22 +020024#include "log.h"
25#include "plugins_types.h"
26#include "tree_data_internal.h"
27#include "tree_schema_internal.h"
28#include "xpath.h"
29
30/**
31 * @brief Check predicate syntax.
32 *
33 * @param[in] ctx libyang context.
34 * @param[in] exp Parsed predicate.
35 * @param[in,out] tok_idx Index in @p exp, is adjusted.
36 * @param[in] prefix Prefix option.
37 * @param[in] pred Predicate option.
38 * @return LY_ERR value.
39 */
40static LY_ERR
41ly_path_check_predicate(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint16_t *tok_idx, uint8_t prefix,
42 uint8_t pred)
43{
44 struct ly_set *set = NULL;
45 uint32_t i;
46 const char *name;
47 size_t name_len;
48
49 /* '[' */
50 if (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1)) {
51 if (((pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_KEYS))
52 && !lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NAMETEST)) {
53 set = ly_set_new();
54 LY_CHECK_ERR_GOTO(!set, LOGMEM(ctx), error);
55
56 do {
57 /* NameTest is always expected here */
58 LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NAMETEST), error);
59
60 /* check prefix based on the options */
61 name = strnstr(exp->expr + exp->tok_pos[*tok_idx], ":", exp->tok_len[*tok_idx]);
62 if ((prefix == LY_PATH_PREFIX_MANDATORY) && !name) {
63 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", exp->tok_len[*tok_idx],
64 exp->expr + exp->tok_pos[*tok_idx]);
65 goto error;
66 }
67 if (!name) {
68 name = exp->expr + exp->tok_pos[*tok_idx];
69 name_len = exp->tok_len[*tok_idx];
70 } else {
71 ++name;
72 name_len = exp->tok_len[*tok_idx] - (name - (exp->expr + exp->tok_pos[*tok_idx]));
73 }
74
75 /* check whether it was not already specified */
76 for (i = 0; i < set->count; ++i) {
77 /* all the keys must be from the same module so this comparison should be fine */
78 if (!strncmp(set->objs[i], name, name_len) && !isalpha(((char *)set->objs[i])[name_len])) {
79 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Duplicate predicate key \"%.*s\" in path.",
80 name_len, name);
81 goto error;
82 }
83 }
84
85 /* add it into the set */
86 ly_set_add(set, (void *)name, LY_SET_OPT_USEASLIST);
87
88 /* NameTest */
89 ++(*tok_idx);
90
91 /* '=' */
92 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), error);
93
94 /* Literal */
95 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL), error);
96
97 /* ']' */
98 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), error);
99
100 /* '[' */
101 } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1));
102
103 /* '.' */
104 } else if ((pred == LY_PATH_PRED_SIMPLE) && !lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_DOT)) {
105 /* '=' */
106 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), error);
107
108 /* Literal */
109 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL), error);
110
111 /* ']' */
112 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), error);
113
114 /* Number */
115 } else if ((pred == LY_PATH_PRED_SIMPLE) && !lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_NUMBER)) {
116
117 /* ']' */
118 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), error);
119
120 } else if ((pred == LY_PATH_PRED_LEAFREF) && !lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NAMETEST)) {
121 assert(prefix == LY_PATH_PREFIX_OPTIONAL);
122 set = ly_set_new();
123 LY_CHECK_ERR_GOTO(!set, LOGMEM(ctx), error);
124
125 do {
126 /* NameTest is always expected here */
127 LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NAMETEST), error);
128
129 name = strnstr(exp->expr + exp->tok_pos[*tok_idx], ":", exp->tok_len[*tok_idx]);
130 if (!name) {
131 name = exp->expr + exp->tok_pos[*tok_idx];
132 name_len = exp->tok_len[*tok_idx];
133 } else {
134 ++name;
135 name_len = exp->tok_len[*tok_idx] - (name - (exp->expr + exp->tok_pos[*tok_idx]));
136 }
137
138 /* check whether it was not already specified */
139 for (i = 0; i < set->count; ++i) {
140 /* all the keys must be from the same module so this comparison should be fine */
141 if (!strncmp(set->objs[i], name, name_len) && !isalpha(((char *)set->objs[i])[name_len])) {
142 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Duplicate predicate key \"%.*s\" in path.",
143 name_len, name);
144 goto error;
145 }
146 }
147
148 /* add it into the set */
149 ly_set_add(set, (void *)name, LY_SET_OPT_USEASLIST);
150
151 /* NameTest */
152 ++(*tok_idx);
153
154 /* '=' */
155 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), error);
156
157 /* FuncName */
158 LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME), error);
159 if ((exp->tok_len[*tok_idx] != 7) || strncmp(exp->expr + exp->tok_pos[*tok_idx], "current", 7)) {
160 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Invalid function \"%.*s\" invocation in path.",
161 exp->tok_len[*tok_idx], exp->expr + exp->tok_pos[*tok_idx]);
162 goto error;
163 }
164 ++(*tok_idx);
165
166 /* '(' */
167 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR1), error);
168
169 /* ')' */
170 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR2), error);
171
172 /* '/' */
173 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_PATH), error);
174
175 /* '..' */
176 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_DDOT), error);
177 do {
178 /* '/' */
179 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_PATH), error);
180 } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_DDOT));
181
182 /* NameTest */
183 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), error);
184
185 /* '/' */
186 while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_OPER_PATH)) {
187 /* NameTest */
188 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), error);
189 }
190
191 /* ']' */
192 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), error);
193
194 /* '[' */
195 } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1));
196
197 } else {
198 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK, lyxp_print_token(exp->tokens[*tok_idx]),
199 exp->expr + exp->tok_pos[*tok_idx]);
200 goto error;
201 }
202 }
203
204 ly_set_free(set, NULL);
205 return LY_SUCCESS;
206
207error:
208 ly_set_free(set, NULL);
209 return LY_EVALID;
210}
211
212LY_ERR
213ly_path_parse(const struct ly_ctx *ctx, const char *str_path, size_t path_len, uint8_t begin, uint8_t lref,
214 uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
215{
216 struct lyxp_expr *exp;
217 uint16_t tok_idx;
218
219 assert((begin == LY_PATH_BEGIN_ABSOLUTE) || (begin == LY_PATH_BEGIN_EITHER));
220 assert((lref == LY_PATH_LREF_TRUE) || (lref == LY_PATH_LREF_FALSE));
221 assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY));
222 assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
223
224 /* parse as a generic XPath expression */
225 exp = lyxp_expr_parse(ctx, str_path, path_len, 1);
226 LY_CHECK_GOTO(!exp, error);
227 tok_idx = 0;
228
229 if (begin == LY_PATH_BEGIN_EITHER) {
230 /* is the path relative? */
231 if (lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH)) {
232 /* '..' */
233 while ((lref == LY_PATH_LREF_TRUE) && !lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_DDOT)) {
234 /* '/' */
235 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), error);
236 }
237 }
238 } else {
239 /* '/' */
240 LY_CHECK_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), error);
241 }
242
243 do {
244 /* NameTest */
245 LY_CHECK_GOTO(lyxp_check_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), error);
246
247 /* check prefix based on the options */
248 if ((prefix == LY_PATH_PREFIX_MANDATORY) && !strnstr(exp->expr + exp->tok_pos[tok_idx], ":", exp->tok_len[tok_idx])) {
249 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", exp->tok_len[tok_idx],
250 exp->expr + exp->tok_pos[tok_idx]);
251 goto error;
252 }
253
254 ++tok_idx;
255
256 /* Predicate* */
257 LY_CHECK_GOTO(ly_path_check_predicate(ctx, exp, &tok_idx, prefix, pred), error);
258
259 /* '/' */
260 } while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH));
261
262 /* trailing token check */
263 if (exp->used > tok_idx) {
264 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of path.",
265 exp->expr + exp->tok_pos[tok_idx]);
266 goto error;
267 }
268
269 *expr = exp;
270 return LY_SUCCESS;
271
272error:
273 lyxp_expr_free(ctx, exp);
274 return LY_EINVAL;
275}
276
277LY_ERR
278ly_path_parse_predicate(const struct ly_ctx *ctx, const char *str_path, size_t path_len, uint8_t prefix, uint8_t pred,
279 struct lyxp_expr **expr)
280{
281 struct lyxp_expr *exp;
282 uint16_t tok_idx;
283
284 assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY));
285 assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
286
287 /* parse as a generic XPath expression */
288 exp = lyxp_expr_parse(ctx, str_path, path_len, 0);
289 LY_CHECK_GOTO(!exp, error);
290 tok_idx = 0;
291
292 LY_CHECK_GOTO(ly_path_check_predicate(ctx, exp, &tok_idx, prefix, pred), error);
293
294 /* trailing token check */
295 if (exp->used > tok_idx) {
296 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of predicate.",
297 exp->expr + exp->tok_pos[tok_idx]);
298 goto error;
299 }
300
301 *expr = exp;
302 return LY_SUCCESS;
303
304error:
305 lyxp_expr_free(ctx, exp);
306 return LY_EINVAL;
307}
308
309/**
310 * @brief Parse prefix from a NameTest, if any, and node name, and return expected module of the node.
311 *
Michal Vasko00cbf532020-06-15 13:58:47 +0200312 * @param[in] ctx libyang context.
Michal Vasko004d3152020-06-11 19:59:22 +0200313 * @param[in] cur_mod Module of the current (original context) node. Needed for ::LYD_SCHEMA.
314 * @param[in] prev_ctx_node Previous context node. Needed for ::LYD_JSON.
315 * @param[in] expr Parsed path.
316 * @param[in] tok_idx Index in @p expr.
317 * @param[in] lref Lref option.
318 * @param[in] resolve_prefix Callback for prefix resolution.
319 * @param[in] prefix_data Data for @p resolve_prefix.
320 * @param[in] format Format of the path.
321 * @param[out] mod Resolved module.
322 * @param[out] name Parsed node name.
323 * @param[out] name_len Length of @p name.
324 * @return LY_ERR value.
325 */
326static LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200327ly_path_compile_prefix(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *prev_ctx_node,
328 const struct lyxp_expr *expr, uint16_t tok_idx, uint8_t lref, ly_clb_resolve_prefix resolve_prefix,
329 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 +0200330{
331 const char *ptr;
332 size_t len;
333
334 assert(expr->tokens[tok_idx] == LYXP_TOKEN_NAMETEST);
335
336 /* get prefix */
337 ptr = strnstr(expr->expr + expr->tok_pos[tok_idx], ":", expr->tok_len[tok_idx]);
338 len = ptr ? ptr - (expr->expr + expr->tok_pos[tok_idx]) : 0;
339
340 /* find next node module */
341 if (ptr) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200342 *mod = resolve_prefix(ctx, expr->expr + expr->tok_pos[tok_idx], len, prefix_data);
Michal Vasko004d3152020-06-11 19:59:22 +0200343 if (!*mod) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200344 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Prefix \"%.*s\" not found of a module in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200345 len, expr->expr + expr->tok_pos[tok_idx]);
346 return LY_EINVAL;
347 } else if (!(*mod)->implemented) {
348 if (lref == LY_PATH_LREF_FALSE) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200349 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
Michal Vasko004d3152020-06-11 19:59:22 +0200350 return LY_EINVAL;
351 }
352 lys_set_implemented_internal((struct lys_module *)*mod, 2);
353 }
354 } else {
355 switch (format) {
356 case LYD_SCHEMA:
357 *mod = cur_mod;
358 break;
359 case LYD_JSON:
360 if (!prev_ctx_node) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200361 LOGINT_RET(ctx);
Michal Vasko004d3152020-06-11 19:59:22 +0200362 }
363 *mod = prev_ctx_node->module;
364 break;
365 case LYD_XML:
Michal Vasko60ea6352020-06-29 13:39:39 +0200366 case LYD_LYB:
Michal Vasko004d3152020-06-11 19:59:22 +0200367 /* not really defined */
Michal Vasko00cbf532020-06-15 13:58:47 +0200368 LOGINT_RET(ctx);
Michal Vasko004d3152020-06-11 19:59:22 +0200369 }
370 }
371
372 /* set name */
373 if (ptr) {
374 *name = ptr + 1;
375 *name_len = expr->tok_len[tok_idx] - len - 1;
376 } else {
377 *name = expr->expr + expr->tok_pos[tok_idx];
378 *name_len = expr->tok_len[tok_idx];
379 }
380
381 return LY_SUCCESS;
382}
383
384LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200385ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
386 const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
387 void *prefix_data, LYD_FORMAT format, struct ly_path_predicate **predicates,
388 enum ly_path_pred_type *pred_type)
Michal Vasko004d3152020-06-11 19:59:22 +0200389{
390 struct ly_path_predicate *p;
391 const struct lysc_node *key;
392 const struct lys_module *mod;
393 const char *name;
394 size_t name_len, key_count;
395
Michal Vasko00cbf532020-06-15 13:58:47 +0200396 assert(ctx && ctx_node);
397
Michal Vasko004d3152020-06-11 19:59:22 +0200398 *pred_type = 0;
399
400 /* '[' */
401 if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
402 /* no predicate */
403 return LY_SUCCESS;
404 }
405
406 if (expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST) {
407 if (ctx_node->nodetype != LYS_LIST) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200408 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200409 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
410 return LY_EINVAL;
411 } else if (ctx_node->flags & LYS_KEYLESS) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200412 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200413 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
414 return LY_EINVAL;
415 }
416
417 do {
418 /* NameTest, find the key */
Michal Vasko00cbf532020-06-15 13:58:47 +0200419 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 +0200420 prefix_data, format, &mod, &name, &name_len));
421 key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
422 if (!key) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200423 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
Michal Vasko004d3152020-06-11 19:59:22 +0200424 return LY_EINVAL;
425 } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200426 LOGVAL(ctx, LY_VLOG_LYSC, key, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200427 lys_nodetype2str(key->nodetype), key->name);
428 return LY_EINVAL;
429 }
430 ++(*tok_idx);
431
432 /* new predicate */
433 if (!*pred_type) {
434 *pred_type = LY_PATH_PREDTYPE_LIST;
435 }
436 assert(*pred_type == LY_PATH_PREDTYPE_LIST);
Michal Vasko00cbf532020-06-15 13:58:47 +0200437 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200438 p->key = key;
439
440 /* '=' */
441 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
442 ++(*tok_idx);
443
444 /* Literal */
445 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL);
446 LY_CHECK_RET(lyd_value_store(&p->value, key, expr->expr + expr->tok_pos[*tok_idx] + 1,
447 expr->tok_len[*tok_idx] - 2, NULL, resolve_prefix, prefix_data, format));
448 ++(*tok_idx);
449
450 /* ']' */
451 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
452 ++(*tok_idx);
453
454 /* another predicate follows? */
455 } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1));
456
457 /* check that all keys were set */
458 key_count = 0;
459 for (key = lysc_node_children(ctx_node, 0); key && (key->flags & LYS_KEY); key = key->next) {
460 ++key_count;
461 }
462 if (LY_ARRAY_SIZE(*predicates) != key_count) {
463 /* names (keys) are unique - it was checked when parsing */
Michal Vasko00cbf532020-06-15 13:58:47 +0200464 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 +0200465 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
Michal Vasko00cbf532020-06-15 13:58:47 +0200466 ly_path_predicates_free(ctx, LY_PATH_PREDTYPE_LIST, NULL, *predicates);
Michal Vasko004d3152020-06-11 19:59:22 +0200467 *predicates = NULL;
468 return LY_EINVAL;
469 }
470
471 } else if (expr->tokens[*tok_idx] == LYXP_TOKEN_DOT) {
472 if (ctx_node->nodetype != LYS_LEAFLIST) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200473 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Leaf-list predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200474 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
475 return LY_EINVAL;
476 }
477 ++(*tok_idx);
478
479 /* new predicate */
480 *pred_type = LY_PATH_PREDTYPE_LEAFLIST;
Michal Vasko00cbf532020-06-15 13:58:47 +0200481 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200482
483 /* '=' */
484 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
485 ++(*tok_idx);
486
487 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL);
488 /* store the value */
489 LY_CHECK_RET(lyd_value_store(&p->value, ctx_node, expr->expr + expr->tok_pos[*tok_idx] + 1,
490 expr->tok_len[*tok_idx] - 2, NULL, resolve_prefix, prefix_data, format));
491 ++(*tok_idx);
492
493 /* ']' */
494 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
495 ++(*tok_idx);
496 } else {
497 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER);
498 if (!(ctx_node->nodetype & (LYS_LEAFLIST | LYS_LIST))) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200499 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200500 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
501 return LY_EINVAL;
502 } else if (ctx_node->flags & LYS_CONFIG_W) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200503 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for configuration"
Michal Vasko004d3152020-06-11 19:59:22 +0200504 " %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
505 return LY_EINVAL;
506 }
Michal Vasko004d3152020-06-11 19:59:22 +0200507
508 /* new predicate */
509 *pred_type = LY_PATH_PREDTYPE_POSITION;
Michal Vasko00cbf532020-06-15 13:58:47 +0200510 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200511
512 /* syntax was already checked */
513 p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&name, 10);
Michal Vasko00cbf532020-06-15 13:58:47 +0200514 ++(*tok_idx);
Michal Vasko004d3152020-06-11 19:59:22 +0200515
516 /* ']' */
517 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
518 ++(*tok_idx);
519 }
520
521 return LY_SUCCESS;
522}
523
524/**
525 * @brief Compile leafref predicate. Actually, it is only checked.
526 *
527 * @param[in] ctx_node Context node, node for which the predicate is defined.
528 * @param[in] cur_node Current (original context) node.
529 * @param[in] expr Parsed path.
530 * @param[in,out] tok_idx Index in @p expr, is adjusted for parsed tokens.
531 * @param[in] resolve_prefix Callback for prefix resolution.
532 * @param[in] prefix_data Data for @p resolve_prefix.
533 * @param[in] format Format of the path.
534 * @return LY_ERR value.
535 */
536static LY_ERR
537ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct lysc_node *cur_node,
538 const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
539 void *prefix_data, LYD_FORMAT format)
540{
541 const struct lysc_node *key, *node, *node2;
542 const struct lys_module *mod;
543 const char *name;
544 size_t name_len;
545
546 /* '[' */
547 if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
548 /* no predicate */
549 return LY_SUCCESS;
550 }
551
552 if (ctx_node->nodetype != LYS_LIST) {
553 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
554 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
555 return LY_EINVAL;
556 } else if (ctx_node->flags & LYS_KEYLESS) {
557 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
558 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
559 return LY_EINVAL;
560 }
561
562 do {
563 /* NameTest, find the key */
Michal Vasko00cbf532020-06-15 13:58:47 +0200564 LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node->module, ctx_node, expr, *tok_idx,
565 LY_PATH_LREF_TRUE, resolve_prefix, prefix_data, format, &mod, &name, &name_len));
Michal Vasko004d3152020-06-11 19:59:22 +0200566 key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
567 if (!key) {
568 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
569 return LY_EINVAL;
570 } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
571 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
572 lys_nodetype2str(key->nodetype), key->name);
573 return LY_EINVAL;
574 }
575 ++(*tok_idx);
576
577 /* we are not actually compiling, throw the key away */
578 (void)key;
579
580 /* '=' */
581 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
582 ++(*tok_idx);
583
584 /* FuncName */
585 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_FUNCNAME);
586 ++(*tok_idx);
587
588 /* evaluating from the "current()" node */
589 node = cur_node;
590
591 /* '(' */
592 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
593 ++(*tok_idx);
594
595 /* ')' */
596 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
597 ++(*tok_idx);
598
599 do {
600 /* '/' */
601 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH);
602 ++(*tok_idx);
603
604 /* go to parent */
605 if (!node) {
606 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Too many parent references in path.");
607 return LY_EINVAL;
608 }
609 node = lysc_data_parent(node);
610
611 /* '..' */
612 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_DDOT);
613 ++(*tok_idx);
614 } while (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_DDOT);
615
616 do {
617 /* '/' */
618 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH);
619 ++(*tok_idx);
620
621 /* NameTest */
622 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST);
Michal Vasko00cbf532020-06-15 13:58:47 +0200623 LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node->module, node, expr, *tok_idx,
624 LY_PATH_LREF_TRUE, resolve_prefix, prefix_data, format, &mod, &name, &name_len));
Michal Vasko004d3152020-06-11 19:59:22 +0200625 node2 = lys_find_child(node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
626 if (!node2) {
627 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.",
628 name_len, name);
629 return LY_EINVAL;
630 }
631 node = node2;
632 ++(*tok_idx);
633 } while ((*tok_idx + 1 < expr->used) && (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_NAMETEST));
634
635 /* check the last target node */
636 if (node->nodetype != LYS_LEAF) {
637 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Leaf expected instead of %s \"%s\" in"
638 " leafref predicate in path.", lys_nodetype2str(node->nodetype), node->name);
639 return LY_EINVAL;
640 }
641
642 /* we are not actually compiling, throw the rightside node away */
643 (void)node;
644
645 /* ']' */
646 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
647 ++(*tok_idx);
648
649 /* another predicate follows? */
650 } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1));
651
652 return LY_SUCCESS;
653}
654
655LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200656ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
657 const struct lyxp_expr *expr, uint8_t lref, uint8_t oper, uint8_t target,
658 ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path)
Michal Vasko004d3152020-06-11 19:59:22 +0200659{
660 LY_ERR ret = LY_SUCCESS;
661 uint16_t tok_idx = 0;
662 const struct lys_module *mod;
663 const struct lysc_node *node2, *cur_node;
Michal Vasko00cbf532020-06-15 13:58:47 +0200664 struct ly_path *p = NULL;
665 int getnext_opts;
Michal Vasko004d3152020-06-11 19:59:22 +0200666 const char *name;
667 size_t name_len;
668
Michal Vasko00cbf532020-06-15 13:58:47 +0200669 assert(ctx);
670 assert((expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) || (lref == LY_PATH_LREF_FALSE) || ctx_node);
Michal Vasko004d3152020-06-11 19:59:22 +0200671 assert((lref == LY_PATH_LREF_TRUE) || (lref == LY_PATH_LREF_FALSE));
Michal Vasko00cbf532020-06-15 13:58:47 +0200672 assert((oper == LY_PATH_OPER_INPUT) || (oper == LY_PATH_OPER_OUTPUT));
673 assert((target == LY_PATH_TARGET_SINGLE) || (target == LY_PATH_TARGET_MANY));
Michal Vasko004d3152020-06-11 19:59:22 +0200674
675 if (lref == LY_PATH_LREF_TRUE) {
676 /* remember original context node */
677 cur_node = ctx_node;
678 }
679 *path = NULL;
680
Michal Vasko00cbf532020-06-15 13:58:47 +0200681 if (oper == LY_PATH_OPER_OUTPUT) {
682 getnext_opts = LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_OUTPUT;
683 } else {
684 getnext_opts = LYS_GETNEXT_NOSTATECHECK;
685 }
686
Michal Vasko004d3152020-06-11 19:59:22 +0200687 if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
688 /* absolute path */
689 ctx_node = NULL;
690
691 ++tok_idx;
692 } else {
693 /* relative path */
694 while ((lref == LY_PATH_LREF_TRUE) && (expr->tokens[tok_idx] == LYXP_TOKEN_DDOT)) {
695 if (!ctx_node) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200696 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Too many parent references in path.");
Michal Vasko004d3152020-06-11 19:59:22 +0200697 return LY_EINVAL;
698 }
699
700 /* get parent */
701 ctx_node = lysc_data_parent(ctx_node);
702
703 ++tok_idx;
704
705 assert(expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH);
706 ++tok_idx;
707 }
708
Michal Vasko00cbf532020-06-15 13:58:47 +0200709 /* we are not storing the parent */
710 (void)ctx_node;
Michal Vasko004d3152020-06-11 19:59:22 +0200711 }
712
713 do {
Michal Vasko00cbf532020-06-15 13:58:47 +0200714 /* check last compiled inner node, whether it is uniquely identified (even key-less list) */
715 if (p && (lref == LY_PATH_LREF_FALSE) && (p->node->nodetype == LYS_LIST) && !p->predicates) {
716 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
717 lys_nodetype2str(p->node->nodetype), p->node->name);
718 return LY_EINVAL;
719 }
720
Michal Vasko004d3152020-06-11 19:59:22 +0200721 /* get module and node name */
Michal Vasko00cbf532020-06-15 13:58:47 +0200722 LY_CHECK_GOTO(ret = ly_path_compile_prefix(ctx, cur_mod, ctx_node, expr, tok_idx, lref, resolve_prefix,
723 prefix_data, format, &mod, &name, &name_len), cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200724 ++tok_idx;
725
726 /* find the next node */
Michal Vasko00cbf532020-06-15 13:58:47 +0200727 node2 = lys_find_child(ctx_node, mod, name, name_len, 0, getnext_opts);
Michal Vasko004d3152020-06-11 19:59:22 +0200728 if (!node2) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200729 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
Michal Vasko004d3152020-06-11 19:59:22 +0200730 ret = LY_EINVAL;
731 goto cleanup;
732 }
733 ctx_node = node2;
734
735 /* new path segment */
Michal Vasko00cbf532020-06-15 13:58:47 +0200736 LY_ARRAY_NEW_GOTO(ctx, *path, p, ret, cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200737 p->node = ctx_node;
738
739 /* compile any predicates */
740 if (lref == LY_PATH_LREF_TRUE) {
741 ret = ly_path_compile_predicate_leafref(ctx_node, cur_node, expr, &tok_idx, resolve_prefix, prefix_data, format);
742 } else {
Michal Vasko00cbf532020-06-15 13:58:47 +0200743 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 +0200744 &p->predicates, &p->pred_type);
745 }
746 LY_CHECK_GOTO(ret, cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200747 } while (!lyxp_next_token(NULL, expr, &tok_idx, LYXP_TOKEN_OPER_PATH));
748
Michal Vasko00cbf532020-06-15 13:58:47 +0200749 /* check last compiled node */
750 if ((lref == LY_PATH_LREF_FALSE) && (target == LY_PATH_TARGET_SINGLE)
751 && (p->node->nodetype & (LYS_LIST | LYS_LEAFLIST)) && !p->predicates) {
752 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
753 lys_nodetype2str(p->node->nodetype), p->node->name);
754 return LY_EINVAL;
755 }
756
Michal Vasko004d3152020-06-11 19:59:22 +0200757cleanup:
758 if (ret) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200759 ly_path_free(ctx, *path);
Michal Vasko004d3152020-06-11 19:59:22 +0200760 *path = NULL;
761 }
762 return ret;
763}
764
765LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200766ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_SIZE_TYPE *path_idx,
767 struct lyd_node **match)
Michal Vasko004d3152020-06-11 19:59:22 +0200768{
769 LY_ARRAY_SIZE_TYPE u;
Michal Vasko00cbf532020-06-15 13:58:47 +0200770 struct lyd_node *prev_node = NULL, *node, *target;
Michal Vasko004d3152020-06-11 19:59:22 +0200771 uint64_t pos;
772
773 assert(path && start);
774
775 if (lysc_data_parent(path[0].node)) {
776 /* relative path, start from the parent children */
Michal Vasko5bfd4be2020-06-23 13:26:19 +0200777 start = lyd_node_children(start, 0);
Michal Vasko004d3152020-06-11 19:59:22 +0200778 } else {
779 /* absolute path, start from the first top-level sibling */
780 while (start->parent) {
781 start = (struct lyd_node *)start->parent;
782 }
783 while (start->prev->next) {
784 start = start->prev;
785 }
786 }
787
788 LY_ARRAY_FOR(path, u) {
789 switch (path[u].pred_type) {
790 case LY_PATH_PREDTYPE_POSITION:
791 /* we cannot use hashes and want an instance on a specific position */
792 pos = 1;
793 node = (struct lyd_node *)start;
794 while (!lyd_find_sibling_next2(node, path[u].node, NULL, 0, &node)) {
795 if (pos == path[u].predicates[0].position) {
796 break;
797 }
798 ++pos;
799 }
800 break;
801 case LY_PATH_PREDTYPE_LEAFLIST:
802 /* we will use hashes to find one leaf-list instance */
803 LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target));
804 lyd_find_sibling_first(start, target, &node);
805 lyd_free_tree(target);
806 break;
807 case LY_PATH_PREDTYPE_LIST:
808 /* we will use hashes to find one list instance */
809 LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, &target));
810 lyd_find_sibling_first(start, target, &node);
811 lyd_free_tree(target);
812 break;
813 case LY_PATH_PREDTYPE_NONE:
814 /* we will use hashes to find one any/container/leaf instance */
815 lyd_find_sibling_val(start, path[u].node, NULL, 0, &node);
816 break;
817 }
818
819 if (!node) {
820 /* no matching nodes */
821 break;
822 }
823
Michal Vasko00cbf532020-06-15 13:58:47 +0200824 /* rememeber previous node */
825 prev_node = node;
826
Michal Vasko004d3152020-06-11 19:59:22 +0200827 /* next path segment, if any */
Michal Vasko5bfd4be2020-06-23 13:26:19 +0200828 start = lyd_node_children(node, 0);
Michal Vasko004d3152020-06-11 19:59:22 +0200829 }
830
Michal Vasko004d3152020-06-11 19:59:22 +0200831 if (node) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200832 /* we have found the full path */
833 if (path_idx) {
834 *path_idx = u;
835 }
836 if (match) {
837 *match = node;
838 }
Michal Vasko004d3152020-06-11 19:59:22 +0200839 return LY_SUCCESS;
Michal Vasko00cbf532020-06-15 13:58:47 +0200840
841 } else if (prev_node) {
842 /* we have found only some partial match */
843 if (path_idx) {
844 *path_idx = u - 1;
845 }
846 if (match) {
847 *match = prev_node;
848 }
849 return LY_EINCOMPLETE;
850 }
851
852 /* we have not found any nodes */
853 if (path_idx) {
854 *path_idx = 0;
855 }
856 if (match) {
857 *match = NULL;
858 }
859 return LY_ENOTFOUND;
860}
861
862LY_ERR
863ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match)
864{
865 LY_ERR ret;
866 struct lyd_node *m;
867
868 ret = ly_path_eval_partial(path, start, NULL, &m);
869
870 if (ret == LY_SUCCESS) {
871 /* last node was found */
872 if (match) {
873 *match = m;
874 }
875 return LY_SUCCESS;
876 }
877
878 /* not a full match */
879 if (match) {
880 *match = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +0200881 }
882 return LY_ENOTFOUND;
883}
884
885LY_ERR
886ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup)
887{
888 LY_ARRAY_SIZE_TYPE u, v;
889
890 if (!path) {
891 return LY_SUCCESS;
892 }
893
894 LY_ARRAY_CREATE_RET(ctx, *dup, LY_ARRAY_SIZE(path), LY_EMEM);
895 LY_ARRAY_FOR(path, u) {
896 LY_ARRAY_INCREMENT(*dup);
897 (*dup)[u].node = path[u].node;
898 if (path[u].predicates) {
899 LY_ARRAY_CREATE_RET(ctx, (*dup)[u].predicates, LY_ARRAY_SIZE(path[u].predicates), LY_EMEM);
900 (*dup)[u].pred_type = path[u].pred_type;
901 LY_ARRAY_FOR(path[u].predicates, v) {
902 struct ly_path_predicate *pred = &path[u].predicates[v];
903
904 LY_ARRAY_INCREMENT((*dup)[u].predicates);
905 switch (path[u].pred_type) {
906 case LY_PATH_PREDTYPE_POSITION:
907 /* position-predicate */
908 (*dup)[u].predicates[v].position = pred->position;
909 break;
910 case LY_PATH_PREDTYPE_LIST:
911 case LY_PATH_PREDTYPE_LEAFLIST:
912 /* key-predicate or leaf-list-predicate */
913 (*dup)[u].predicates[v].key = pred->key;
914 (*dup)[u].predicates[v].value.realtype = pred->value.realtype;
915 pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].predicates[v].value);
916 break;
917 case LY_PATH_PREDTYPE_NONE:
918 break;
919 }
920 }
921 }
922 }
923
924 return LY_SUCCESS;
925}
926
927void
928ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type, const struct lysc_node *llist,
929 struct ly_path_predicate *predicates)
930{
931 LY_ARRAY_SIZE_TYPE u;
932
933 if (!predicates) {
934 return;
935 }
936
937 LY_ARRAY_FOR(predicates, u) {
938 switch (pred_type) {
939 case LY_PATH_PREDTYPE_POSITION:
940 case LY_PATH_PREDTYPE_NONE:
941 /* nothing to free */
942 break;
943 case LY_PATH_PREDTYPE_LIST:
944 ((struct lysc_node_leaf *)predicates[u].key)->type->plugin->free(ctx, &predicates[u].value);
945 break;
946 case LY_PATH_PREDTYPE_LEAFLIST:
947 ((struct lysc_node_leaflist *)llist)->type->plugin->free(ctx, &predicates[u].value);
948 break;
949 }
950 }
951 LY_ARRAY_FREE(predicates);
952}
953
954void
955ly_path_free(const struct ly_ctx *ctx, struct ly_path *path)
956{
957 LY_ARRAY_SIZE_TYPE u;
958
959 LY_ARRAY_FOR(path, u) {
960 ly_path_predicates_free(ctx, path[u].pred_type, path[u].node, path[u].predicates);
961 }
962 LY_ARRAY_FREE(path);
963}