blob: e72485b94d26d6b2d784e1fbbaa2e70cce899252 [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:
366 /* not really defined */
Michal Vasko00cbf532020-06-15 13:58:47 +0200367 LOGINT_RET(ctx);
Michal Vasko004d3152020-06-11 19:59:22 +0200368 }
369 }
370
371 /* set name */
372 if (ptr) {
373 *name = ptr + 1;
374 *name_len = expr->tok_len[tok_idx] - len - 1;
375 } else {
376 *name = expr->expr + expr->tok_pos[tok_idx];
377 *name_len = expr->tok_len[tok_idx];
378 }
379
380 return LY_SUCCESS;
381}
382
383LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200384ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
385 const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
386 void *prefix_data, LYD_FORMAT format, struct ly_path_predicate **predicates,
387 enum ly_path_pred_type *pred_type)
Michal Vasko004d3152020-06-11 19:59:22 +0200388{
389 struct ly_path_predicate *p;
390 const struct lysc_node *key;
391 const struct lys_module *mod;
392 const char *name;
393 size_t name_len, key_count;
394
Michal Vasko00cbf532020-06-15 13:58:47 +0200395 assert(ctx && ctx_node);
396
Michal Vasko004d3152020-06-11 19:59:22 +0200397 *pred_type = 0;
398
399 /* '[' */
400 if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
401 /* no predicate */
402 return LY_SUCCESS;
403 }
404
405 if (expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST) {
406 if (ctx_node->nodetype != LYS_LIST) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200407 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200408 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
409 return LY_EINVAL;
410 } else if (ctx_node->flags & LYS_KEYLESS) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200411 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200412 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
413 return LY_EINVAL;
414 }
415
416 do {
417 /* NameTest, find the key */
Michal Vasko00cbf532020-06-15 13:58:47 +0200418 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 +0200419 prefix_data, format, &mod, &name, &name_len));
420 key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
421 if (!key) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200422 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
Michal Vasko004d3152020-06-11 19:59:22 +0200423 return LY_EINVAL;
424 } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200425 LOGVAL(ctx, LY_VLOG_LYSC, key, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200426 lys_nodetype2str(key->nodetype), key->name);
427 return LY_EINVAL;
428 }
429 ++(*tok_idx);
430
431 /* new predicate */
432 if (!*pred_type) {
433 *pred_type = LY_PATH_PREDTYPE_LIST;
434 }
435 assert(*pred_type == LY_PATH_PREDTYPE_LIST);
Michal Vasko00cbf532020-06-15 13:58:47 +0200436 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200437 p->key = key;
438
439 /* '=' */
440 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
441 ++(*tok_idx);
442
443 /* Literal */
444 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL);
445 LY_CHECK_RET(lyd_value_store(&p->value, key, expr->expr + expr->tok_pos[*tok_idx] + 1,
446 expr->tok_len[*tok_idx] - 2, NULL, resolve_prefix, prefix_data, format));
447 ++(*tok_idx);
448
449 /* ']' */
450 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
451 ++(*tok_idx);
452
453 /* another predicate follows? */
454 } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1));
455
456 /* check that all keys were set */
457 key_count = 0;
458 for (key = lysc_node_children(ctx_node, 0); key && (key->flags & LYS_KEY); key = key->next) {
459 ++key_count;
460 }
461 if (LY_ARRAY_SIZE(*predicates) != key_count) {
462 /* names (keys) are unique - it was checked when parsing */
Michal Vasko00cbf532020-06-15 13:58:47 +0200463 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 +0200464 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
Michal Vasko00cbf532020-06-15 13:58:47 +0200465 ly_path_predicates_free(ctx, LY_PATH_PREDTYPE_LIST, NULL, *predicates);
Michal Vasko004d3152020-06-11 19:59:22 +0200466 *predicates = NULL;
467 return LY_EINVAL;
468 }
469
470 } else if (expr->tokens[*tok_idx] == LYXP_TOKEN_DOT) {
471 if (ctx_node->nodetype != LYS_LEAFLIST) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200472 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Leaf-list predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200473 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
474 return LY_EINVAL;
475 }
476 ++(*tok_idx);
477
478 /* new predicate */
479 *pred_type = LY_PATH_PREDTYPE_LEAFLIST;
Michal Vasko00cbf532020-06-15 13:58:47 +0200480 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200481
482 /* '=' */
483 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
484 ++(*tok_idx);
485
486 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL);
487 /* store the value */
488 LY_CHECK_RET(lyd_value_store(&p->value, ctx_node, expr->expr + expr->tok_pos[*tok_idx] + 1,
489 expr->tok_len[*tok_idx] - 2, NULL, resolve_prefix, prefix_data, format));
490 ++(*tok_idx);
491
492 /* ']' */
493 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
494 ++(*tok_idx);
495 } else {
496 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER);
497 if (!(ctx_node->nodetype & (LYS_LEAFLIST | LYS_LIST))) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200498 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for %s \"%s\" in path.",
Michal Vasko004d3152020-06-11 19:59:22 +0200499 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
500 return LY_EINVAL;
501 } else if (ctx_node->flags & LYS_CONFIG_W) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200502 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for configuration"
Michal Vasko004d3152020-06-11 19:59:22 +0200503 " %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
504 return LY_EINVAL;
505 }
Michal Vasko004d3152020-06-11 19:59:22 +0200506
507 /* new predicate */
508 *pred_type = LY_PATH_PREDTYPE_POSITION;
Michal Vasko00cbf532020-06-15 13:58:47 +0200509 LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
Michal Vasko004d3152020-06-11 19:59:22 +0200510
511 /* syntax was already checked */
512 p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&name, 10);
Michal Vasko00cbf532020-06-15 13:58:47 +0200513 ++(*tok_idx);
Michal Vasko004d3152020-06-11 19:59:22 +0200514
515 /* ']' */
516 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
517 ++(*tok_idx);
518 }
519
520 return LY_SUCCESS;
521}
522
523/**
524 * @brief Compile leafref predicate. Actually, it is only checked.
525 *
526 * @param[in] ctx_node Context node, node for which the predicate is defined.
527 * @param[in] cur_node Current (original context) node.
528 * @param[in] expr Parsed path.
529 * @param[in,out] tok_idx Index in @p expr, is adjusted for parsed tokens.
530 * @param[in] resolve_prefix Callback for prefix resolution.
531 * @param[in] prefix_data Data for @p resolve_prefix.
532 * @param[in] format Format of the path.
533 * @return LY_ERR value.
534 */
535static LY_ERR
536ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct lysc_node *cur_node,
537 const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
538 void *prefix_data, LYD_FORMAT format)
539{
540 const struct lysc_node *key, *node, *node2;
541 const struct lys_module *mod;
542 const char *name;
543 size_t name_len;
544
545 /* '[' */
546 if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
547 /* no predicate */
548 return LY_SUCCESS;
549 }
550
551 if (ctx_node->nodetype != LYS_LIST) {
552 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
553 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
554 return LY_EINVAL;
555 } else if (ctx_node->flags & LYS_KEYLESS) {
556 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
557 lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
558 return LY_EINVAL;
559 }
560
561 do {
562 /* NameTest, find the key */
Michal Vasko00cbf532020-06-15 13:58:47 +0200563 LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node->module, ctx_node, expr, *tok_idx,
564 LY_PATH_LREF_TRUE, resolve_prefix, prefix_data, format, &mod, &name, &name_len));
Michal Vasko004d3152020-06-11 19:59:22 +0200565 key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
566 if (!key) {
567 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
568 return LY_EINVAL;
569 } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
570 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
571 lys_nodetype2str(key->nodetype), key->name);
572 return LY_EINVAL;
573 }
574 ++(*tok_idx);
575
576 /* we are not actually compiling, throw the key away */
577 (void)key;
578
579 /* '=' */
580 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
581 ++(*tok_idx);
582
583 /* FuncName */
584 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_FUNCNAME);
585 ++(*tok_idx);
586
587 /* evaluating from the "current()" node */
588 node = cur_node;
589
590 /* '(' */
591 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
592 ++(*tok_idx);
593
594 /* ')' */
595 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
596 ++(*tok_idx);
597
598 do {
599 /* '/' */
600 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH);
601 ++(*tok_idx);
602
603 /* go to parent */
604 if (!node) {
605 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Too many parent references in path.");
606 return LY_EINVAL;
607 }
608 node = lysc_data_parent(node);
609
610 /* '..' */
611 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_DDOT);
612 ++(*tok_idx);
613 } while (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_DDOT);
614
615 do {
616 /* '/' */
617 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH);
618 ++(*tok_idx);
619
620 /* NameTest */
621 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST);
Michal Vasko00cbf532020-06-15 13:58:47 +0200622 LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node->module, node, expr, *tok_idx,
623 LY_PATH_LREF_TRUE, resolve_prefix, prefix_data, format, &mod, &name, &name_len));
Michal Vasko004d3152020-06-11 19:59:22 +0200624 node2 = lys_find_child(node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
625 if (!node2) {
626 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.",
627 name_len, name);
628 return LY_EINVAL;
629 }
630 node = node2;
631 ++(*tok_idx);
632 } while ((*tok_idx + 1 < expr->used) && (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_NAMETEST));
633
634 /* check the last target node */
635 if (node->nodetype != LYS_LEAF) {
636 LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Leaf expected instead of %s \"%s\" in"
637 " leafref predicate in path.", lys_nodetype2str(node->nodetype), node->name);
638 return LY_EINVAL;
639 }
640
641 /* we are not actually compiling, throw the rightside node away */
642 (void)node;
643
644 /* ']' */
645 assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
646 ++(*tok_idx);
647
648 /* another predicate follows? */
649 } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1));
650
651 return LY_SUCCESS;
652}
653
654LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200655ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
656 const struct lyxp_expr *expr, uint8_t lref, uint8_t oper, uint8_t target,
657 ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path)
Michal Vasko004d3152020-06-11 19:59:22 +0200658{
659 LY_ERR ret = LY_SUCCESS;
660 uint16_t tok_idx = 0;
661 const struct lys_module *mod;
662 const struct lysc_node *node2, *cur_node;
Michal Vasko00cbf532020-06-15 13:58:47 +0200663 struct ly_path *p = NULL;
664 int getnext_opts;
Michal Vasko004d3152020-06-11 19:59:22 +0200665 const char *name;
666 size_t name_len;
667
Michal Vasko00cbf532020-06-15 13:58:47 +0200668 assert(ctx);
669 assert((expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) || (lref == LY_PATH_LREF_FALSE) || ctx_node);
Michal Vasko004d3152020-06-11 19:59:22 +0200670 assert((lref == LY_PATH_LREF_TRUE) || (lref == LY_PATH_LREF_FALSE));
Michal Vasko00cbf532020-06-15 13:58:47 +0200671 assert((oper == LY_PATH_OPER_INPUT) || (oper == LY_PATH_OPER_OUTPUT));
672 assert((target == LY_PATH_TARGET_SINGLE) || (target == LY_PATH_TARGET_MANY));
Michal Vasko004d3152020-06-11 19:59:22 +0200673
674 if (lref == LY_PATH_LREF_TRUE) {
675 /* remember original context node */
676 cur_node = ctx_node;
677 }
678 *path = NULL;
679
Michal Vasko00cbf532020-06-15 13:58:47 +0200680 if (oper == LY_PATH_OPER_OUTPUT) {
681 getnext_opts = LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_OUTPUT;
682 } else {
683 getnext_opts = LYS_GETNEXT_NOSTATECHECK;
684 }
685
Michal Vasko004d3152020-06-11 19:59:22 +0200686 if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
687 /* absolute path */
688 ctx_node = NULL;
689
690 ++tok_idx;
691 } else {
692 /* relative path */
693 while ((lref == LY_PATH_LREF_TRUE) && (expr->tokens[tok_idx] == LYXP_TOKEN_DDOT)) {
694 if (!ctx_node) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200695 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Too many parent references in path.");
Michal Vasko004d3152020-06-11 19:59:22 +0200696 return LY_EINVAL;
697 }
698
699 /* get parent */
700 ctx_node = lysc_data_parent(ctx_node);
701
702 ++tok_idx;
703
704 assert(expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH);
705 ++tok_idx;
706 }
707
Michal Vasko00cbf532020-06-15 13:58:47 +0200708 /* we are not storing the parent */
709 (void)ctx_node;
Michal Vasko004d3152020-06-11 19:59:22 +0200710 }
711
712 do {
Michal Vasko00cbf532020-06-15 13:58:47 +0200713 /* check last compiled inner node, whether it is uniquely identified (even key-less list) */
714 if (p && (lref == LY_PATH_LREF_FALSE) && (p->node->nodetype == LYS_LIST) && !p->predicates) {
715 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
716 lys_nodetype2str(p->node->nodetype), p->node->name);
717 return LY_EINVAL;
718 }
719
Michal Vasko004d3152020-06-11 19:59:22 +0200720 /* get module and node name */
Michal Vasko00cbf532020-06-15 13:58:47 +0200721 LY_CHECK_GOTO(ret = ly_path_compile_prefix(ctx, cur_mod, ctx_node, expr, tok_idx, lref, resolve_prefix,
722 prefix_data, format, &mod, &name, &name_len), cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200723 ++tok_idx;
724
725 /* find the next node */
Michal Vasko00cbf532020-06-15 13:58:47 +0200726 node2 = lys_find_child(ctx_node, mod, name, name_len, 0, getnext_opts);
Michal Vasko004d3152020-06-11 19:59:22 +0200727 if (!node2) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200728 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
Michal Vasko004d3152020-06-11 19:59:22 +0200729 ret = LY_EINVAL;
730 goto cleanup;
731 }
732 ctx_node = node2;
733
734 /* new path segment */
Michal Vasko00cbf532020-06-15 13:58:47 +0200735 LY_ARRAY_NEW_GOTO(ctx, *path, p, ret, cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200736 p->node = ctx_node;
737
738 /* compile any predicates */
739 if (lref == LY_PATH_LREF_TRUE) {
740 ret = ly_path_compile_predicate_leafref(ctx_node, cur_node, expr, &tok_idx, resolve_prefix, prefix_data, format);
741 } else {
Michal Vasko00cbf532020-06-15 13:58:47 +0200742 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 +0200743 &p->predicates, &p->pred_type);
744 }
745 LY_CHECK_GOTO(ret, cleanup);
Michal Vasko004d3152020-06-11 19:59:22 +0200746 } while (!lyxp_next_token(NULL, expr, &tok_idx, LYXP_TOKEN_OPER_PATH));
747
Michal Vasko00cbf532020-06-15 13:58:47 +0200748 /* check last compiled node */
749 if ((lref == LY_PATH_LREF_FALSE) && (target == LY_PATH_TARGET_SINGLE)
750 && (p->node->nodetype & (LYS_LIST | LYS_LEAFLIST)) && !p->predicates) {
751 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
752 lys_nodetype2str(p->node->nodetype), p->node->name);
753 return LY_EINVAL;
754 }
755
Michal Vasko004d3152020-06-11 19:59:22 +0200756cleanup:
757 if (ret) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200758 ly_path_free(ctx, *path);
Michal Vasko004d3152020-06-11 19:59:22 +0200759 *path = NULL;
760 }
761 return ret;
762}
763
764LY_ERR
Michal Vasko00cbf532020-06-15 13:58:47 +0200765ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_SIZE_TYPE *path_idx,
766 struct lyd_node **match)
Michal Vasko004d3152020-06-11 19:59:22 +0200767{
768 LY_ARRAY_SIZE_TYPE u;
Michal Vasko00cbf532020-06-15 13:58:47 +0200769 struct lyd_node *prev_node = NULL, *node, *target;
Michal Vasko004d3152020-06-11 19:59:22 +0200770 uint64_t pos;
771
772 assert(path && start);
773
774 if (lysc_data_parent(path[0].node)) {
775 /* relative path, start from the parent children */
Michal Vasko5bfd4be2020-06-23 13:26:19 +0200776 start = lyd_node_children(start, 0);
Michal Vasko004d3152020-06-11 19:59:22 +0200777 } else {
778 /* absolute path, start from the first top-level sibling */
779 while (start->parent) {
780 start = (struct lyd_node *)start->parent;
781 }
782 while (start->prev->next) {
783 start = start->prev;
784 }
785 }
786
787 LY_ARRAY_FOR(path, u) {
788 switch (path[u].pred_type) {
789 case LY_PATH_PREDTYPE_POSITION:
790 /* we cannot use hashes and want an instance on a specific position */
791 pos = 1;
792 node = (struct lyd_node *)start;
793 while (!lyd_find_sibling_next2(node, path[u].node, NULL, 0, &node)) {
794 if (pos == path[u].predicates[0].position) {
795 break;
796 }
797 ++pos;
798 }
799 break;
800 case LY_PATH_PREDTYPE_LEAFLIST:
801 /* we will use hashes to find one leaf-list instance */
802 LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target));
803 lyd_find_sibling_first(start, target, &node);
804 lyd_free_tree(target);
805 break;
806 case LY_PATH_PREDTYPE_LIST:
807 /* we will use hashes to find one list instance */
808 LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, &target));
809 lyd_find_sibling_first(start, target, &node);
810 lyd_free_tree(target);
811 break;
812 case LY_PATH_PREDTYPE_NONE:
813 /* we will use hashes to find one any/container/leaf instance */
814 lyd_find_sibling_val(start, path[u].node, NULL, 0, &node);
815 break;
816 }
817
818 if (!node) {
819 /* no matching nodes */
820 break;
821 }
822
Michal Vasko00cbf532020-06-15 13:58:47 +0200823 /* rememeber previous node */
824 prev_node = node;
825
Michal Vasko004d3152020-06-11 19:59:22 +0200826 /* next path segment, if any */
Michal Vasko5bfd4be2020-06-23 13:26:19 +0200827 start = lyd_node_children(node, 0);
Michal Vasko004d3152020-06-11 19:59:22 +0200828 }
829
Michal Vasko004d3152020-06-11 19:59:22 +0200830 if (node) {
Michal Vasko00cbf532020-06-15 13:58:47 +0200831 /* we have found the full path */
832 if (path_idx) {
833 *path_idx = u;
834 }
835 if (match) {
836 *match = node;
837 }
Michal Vasko004d3152020-06-11 19:59:22 +0200838 return LY_SUCCESS;
Michal Vasko00cbf532020-06-15 13:58:47 +0200839
840 } else if (prev_node) {
841 /* we have found only some partial match */
842 if (path_idx) {
843 *path_idx = u - 1;
844 }
845 if (match) {
846 *match = prev_node;
847 }
848 return LY_EINCOMPLETE;
849 }
850
851 /* we have not found any nodes */
852 if (path_idx) {
853 *path_idx = 0;
854 }
855 if (match) {
856 *match = NULL;
857 }
858 return LY_ENOTFOUND;
859}
860
861LY_ERR
862ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match)
863{
864 LY_ERR ret;
865 struct lyd_node *m;
866
867 ret = ly_path_eval_partial(path, start, NULL, &m);
868
869 if (ret == LY_SUCCESS) {
870 /* last node was found */
871 if (match) {
872 *match = m;
873 }
874 return LY_SUCCESS;
875 }
876
877 /* not a full match */
878 if (match) {
879 *match = NULL;
Michal Vasko004d3152020-06-11 19:59:22 +0200880 }
881 return LY_ENOTFOUND;
882}
883
884LY_ERR
885ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup)
886{
887 LY_ARRAY_SIZE_TYPE u, v;
888
889 if (!path) {
890 return LY_SUCCESS;
891 }
892
893 LY_ARRAY_CREATE_RET(ctx, *dup, LY_ARRAY_SIZE(path), LY_EMEM);
894 LY_ARRAY_FOR(path, u) {
895 LY_ARRAY_INCREMENT(*dup);
896 (*dup)[u].node = path[u].node;
897 if (path[u].predicates) {
898 LY_ARRAY_CREATE_RET(ctx, (*dup)[u].predicates, LY_ARRAY_SIZE(path[u].predicates), LY_EMEM);
899 (*dup)[u].pred_type = path[u].pred_type;
900 LY_ARRAY_FOR(path[u].predicates, v) {
901 struct ly_path_predicate *pred = &path[u].predicates[v];
902
903 LY_ARRAY_INCREMENT((*dup)[u].predicates);
904 switch (path[u].pred_type) {
905 case LY_PATH_PREDTYPE_POSITION:
906 /* position-predicate */
907 (*dup)[u].predicates[v].position = pred->position;
908 break;
909 case LY_PATH_PREDTYPE_LIST:
910 case LY_PATH_PREDTYPE_LEAFLIST:
911 /* key-predicate or leaf-list-predicate */
912 (*dup)[u].predicates[v].key = pred->key;
913 (*dup)[u].predicates[v].value.realtype = pred->value.realtype;
914 pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].predicates[v].value);
915 break;
916 case LY_PATH_PREDTYPE_NONE:
917 break;
918 }
919 }
920 }
921 }
922
923 return LY_SUCCESS;
924}
925
926void
927ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type, const struct lysc_node *llist,
928 struct ly_path_predicate *predicates)
929{
930 LY_ARRAY_SIZE_TYPE u;
931
932 if (!predicates) {
933 return;
934 }
935
936 LY_ARRAY_FOR(predicates, u) {
937 switch (pred_type) {
938 case LY_PATH_PREDTYPE_POSITION:
939 case LY_PATH_PREDTYPE_NONE:
940 /* nothing to free */
941 break;
942 case LY_PATH_PREDTYPE_LIST:
943 ((struct lysc_node_leaf *)predicates[u].key)->type->plugin->free(ctx, &predicates[u].value);
944 break;
945 case LY_PATH_PREDTYPE_LEAFLIST:
946 ((struct lysc_node_leaflist *)llist)->type->plugin->free(ctx, &predicates[u].value);
947 break;
948 }
949 }
950 LY_ARRAY_FREE(predicates);
951}
952
953void
954ly_path_free(const struct ly_ctx *ctx, struct ly_path *path)
955{
956 LY_ARRAY_SIZE_TYPE u;
957
958 LY_ARRAY_FOR(path, u) {
959 ly_path_predicates_free(ctx, path[u].pred_type, path[u].node, path[u].predicates);
960 }
961 LY_ARRAY_FREE(path);
962}