Line | Count | Source |
1 | | /** |
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 | | #include <string.h> |
22 | | |
23 | | #include "common.h" |
24 | | #include "compat.h" |
25 | | #include "log.h" |
26 | | #include "plugins_types.h" |
27 | | #include "schema_compile.h" |
28 | | #include "set.h" |
29 | | #include "tree.h" |
30 | | #include "tree_data_internal.h" |
31 | | #include "tree_edit.h" |
32 | | #include "tree_schema.h" |
33 | | #include "tree_schema_internal.h" |
34 | | #include "xpath.h" |
35 | | |
36 | | #define LOGVAL_P(CTX, CUR_NODE, CODE, ...) ly_vlog(CTX, (CUR_NODE) ? LY_VLOG_LYSC : LY_VLOG_NONE, CUR_NODE, CODE, ##__VA_ARGS__) |
37 | | |
38 | | /** |
39 | | * @brief Check predicate syntax. |
40 | | * |
41 | | * @param[in] ctx libyang context. |
42 | | * @param[in] cur_node Current (original context) node. |
43 | | * @param[in] exp Parsed predicate. |
44 | | * @param[in,out] tok_idx Index in @p exp, is adjusted. |
45 | | * @param[in] prefix Prefix option. |
46 | | * @param[in] pred Predicate option. |
47 | | * @return LY_ERR value. |
48 | | */ |
49 | | static LY_ERR |
50 | | ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lyxp_expr *exp, |
51 | | uint16_t *tok_idx, uint8_t prefix, uint8_t pred) |
52 | 0 | { |
53 | 0 | LY_ERR ret = LY_SUCCESS; |
54 | 0 | struct ly_set *set = NULL; |
55 | 0 | uint32_t i; |
56 | 0 | const char *name; |
57 | 0 | size_t name_len; |
58 | |
|
59 | 0 | LOG_LOCSET(cur_node, NULL, NULL, NULL); |
60 | |
|
61 | 0 | if (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1)) { |
62 | | /* '[' */ |
63 | |
|
64 | 0 | if (((pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_KEYS)) && |
65 | 0 | !lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NAMETEST)) { |
66 | 0 | ret = ly_set_new(&set); |
67 | 0 | LY_CHECK_GOTO(ret, cleanup); |
68 | |
|
69 | 0 | do { |
70 | | /* NameTest is always expected here */ |
71 | 0 | LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NAMETEST), token_error); |
72 | | |
73 | | /* check prefix based on the options */ |
74 | 0 | name = strnstr(exp->expr + exp->tok_pos[*tok_idx], ":", exp->tok_len[*tok_idx]); |
75 | 0 | if ((prefix == LY_PATH_PREFIX_MANDATORY) && !name) { |
76 | 0 | LOGVAL(ctx, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", exp->tok_len[*tok_idx], |
77 | 0 | exp->expr + exp->tok_pos[*tok_idx]); |
78 | 0 | goto token_error; |
79 | 0 | } else if ((prefix == LY_PATH_PREFIX_STRICT_INHERIT) && name) { |
80 | 0 | LOGVAL(ctx, LYVE_XPATH, "Redundant prefix for \"%.*s\" in path.", exp->tok_len[*tok_idx], |
81 | 0 | exp->expr + exp->tok_pos[*tok_idx]); |
82 | 0 | goto token_error; |
83 | 0 | } |
84 | 0 | if (!name) { |
85 | 0 | name = exp->expr + exp->tok_pos[*tok_idx]; |
86 | 0 | name_len = exp->tok_len[*tok_idx]; |
87 | 0 | } else { |
88 | 0 | ++name; |
89 | 0 | name_len = exp->tok_len[*tok_idx] - (name - (exp->expr + exp->tok_pos[*tok_idx])); |
90 | 0 | } |
91 | | |
92 | | /* check whether it was not already specified */ |
93 | 0 | for (i = 0; i < set->count; ++i) { |
94 | | /* all the keys must be from the same module so this comparison should be fine */ |
95 | 0 | if (!strncmp(set->objs[i], name, name_len) && !isalpha(((char *)set->objs[i])[name_len])) { |
96 | 0 | LOGVAL(ctx, LYVE_XPATH, "Duplicate predicate key \"%.*s\" in path.", (int)name_len, name); |
97 | 0 | goto token_error; |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | | /* add it into the set */ |
102 | 0 | ret = ly_set_add(set, (void *)name, 1, NULL); |
103 | 0 | LY_CHECK_GOTO(ret, cleanup); |
104 | | |
105 | | /* NameTest */ |
106 | 0 | ++(*tok_idx); |
107 | | |
108 | | /* '=' */ |
109 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error); |
110 | | |
111 | | /* Literal */ |
112 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL), token_error); |
113 | | |
114 | | /* ']' */ |
115 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error); |
116 | | |
117 | | /* '[' */ |
118 | 0 | } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1)); |
119 | |
|
120 | 0 | } else if ((pred == LY_PATH_PRED_SIMPLE) && !lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_DOT)) { |
121 | | /* '.' */ |
122 | | |
123 | | /* '=' */ |
124 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error); |
125 | | |
126 | | /* Literal */ |
127 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL), token_error); |
128 | | |
129 | | /* ']' */ |
130 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error); |
131 | |
|
132 | 0 | } else if ((pred == LY_PATH_PRED_SIMPLE) && !lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_NUMBER)) { |
133 | | /* Number */ |
134 | | |
135 | | /* ']' */ |
136 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error); |
137 | |
|
138 | 0 | } else if ((pred == LY_PATH_PRED_LEAFREF) && !lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NAMETEST)) { |
139 | 0 | assert(prefix == LY_PATH_PREFIX_OPTIONAL); |
140 | 0 | ret = ly_set_new(&set); |
141 | 0 | LY_CHECK_GOTO(ret, cleanup); |
142 | |
|
143 | 0 | do { |
144 | | /* NameTest is always expected here */ |
145 | 0 | LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NAMETEST), token_error); |
146 | |
|
147 | 0 | name = strnstr(exp->expr + exp->tok_pos[*tok_idx], ":", exp->tok_len[*tok_idx]); |
148 | 0 | if (!name) { |
149 | 0 | name = exp->expr + exp->tok_pos[*tok_idx]; |
150 | 0 | name_len = exp->tok_len[*tok_idx]; |
151 | 0 | } else { |
152 | 0 | ++name; |
153 | 0 | name_len = exp->tok_len[*tok_idx] - (name - (exp->expr + exp->tok_pos[*tok_idx])); |
154 | 0 | } |
155 | | |
156 | | /* check whether it was not already specified */ |
157 | 0 | for (i = 0; i < set->count; ++i) { |
158 | | /* all the keys must be from the same module so this comparison should be fine */ |
159 | 0 | if (!strncmp(set->objs[i], name, name_len) && !isalpha(((char *)set->objs[i])[name_len])) { |
160 | 0 | LOGVAL(ctx, LYVE_XPATH, "Duplicate predicate key \"%.*s\" in path.", (int)name_len, name); |
161 | 0 | goto token_error; |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | /* add it into the set */ |
166 | 0 | ret = ly_set_add(set, (void *)name, 1, NULL); |
167 | 0 | LY_CHECK_GOTO(ret, cleanup); |
168 | | |
169 | | /* NameTest */ |
170 | 0 | ++(*tok_idx); |
171 | | |
172 | | /* '=' */ |
173 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error); |
174 | | |
175 | | /* FuncName */ |
176 | 0 | LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME), token_error); |
177 | 0 | if ((exp->tok_len[*tok_idx] != ly_strlen_const("current")) || |
178 | 0 | strncmp(exp->expr + exp->tok_pos[*tok_idx], "current", ly_strlen_const("current"))) { |
179 | 0 | LOGVAL(ctx, LYVE_XPATH, "Invalid function \"%.*s\" invocation in path.", |
180 | 0 | exp->tok_len[*tok_idx], exp->expr + exp->tok_pos[*tok_idx]); |
181 | 0 | goto token_error; |
182 | 0 | } |
183 | 0 | ++(*tok_idx); |
184 | | |
185 | | /* '(' */ |
186 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR1), token_error); |
187 | | |
188 | | /* ')' */ |
189 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR2), token_error); |
190 | | |
191 | | /* '/' */ |
192 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_PATH), token_error); |
193 | | |
194 | | /* '..' */ |
195 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_DDOT), token_error); |
196 | 0 | do { |
197 | | /* '/' */ |
198 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_PATH), token_error); |
199 | 0 | } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_DDOT)); |
200 | | |
201 | | /* NameTest */ |
202 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), token_error); |
203 | | |
204 | | /* '/' */ |
205 | 0 | while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_OPER_PATH)) { |
206 | | /* NameTest */ |
207 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), token_error); |
208 | 0 | } |
209 | | |
210 | | /* ']' */ |
211 | 0 | LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error); |
212 | | |
213 | | /* '[' */ |
214 | 0 | } while (!lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_BRACK1)); |
215 | |
|
216 | 0 | } else { |
217 | 0 | LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_print_token(exp->tokens[*tok_idx]), exp->expr + exp->tok_pos[*tok_idx]); |
218 | 0 | goto token_error; |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | 0 | cleanup: |
223 | 0 | LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0); |
224 | 0 | ly_set_free(set, NULL); |
225 | 0 | return ret; |
226 | | |
227 | 0 | token_error: |
228 | 0 | LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0); |
229 | 0 | ly_set_free(set, NULL); |
230 | 0 | return LY_EVALID; |
231 | 0 | } |
232 | | |
233 | | LY_ERR |
234 | | ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len, |
235 | | uint8_t begin, uint8_t lref, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr) |
236 | 0 | { |
237 | 0 | LY_ERR ret = LY_SUCCESS; |
238 | 0 | struct lyxp_expr *exp = NULL; |
239 | 0 | uint16_t tok_idx, cur_len; |
240 | 0 | const char *cur_node, *prev_prefix = NULL, *ptr; |
241 | |
|
242 | 0 | assert((begin == LY_PATH_BEGIN_ABSOLUTE) || (begin == LY_PATH_BEGIN_EITHER)); |
243 | 0 | assert((lref == LY_PATH_LREF_TRUE) || (lref == LY_PATH_LREF_FALSE)); |
244 | 0 | assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY) || |
245 | 0 | (prefix == LY_PATH_PREFIX_STRICT_INHERIT)); |
246 | 0 | assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF)); |
247 | |
|
248 | 0 | LOG_LOCSET(ctx_node, NULL, NULL, NULL); |
249 | | |
250 | | /* parse as a generic XPath expression */ |
251 | 0 | LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 1, &exp), error); |
252 | 0 | tok_idx = 0; |
253 | |
|
254 | 0 | if (begin == LY_PATH_BEGIN_EITHER) { |
255 | | /* is the path relative? */ |
256 | 0 | if (lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH)) { |
257 | | /* relative path check specific to leafref */ |
258 | 0 | if (lref == LY_PATH_LREF_TRUE) { |
259 | | /* mandatory '..' */ |
260 | 0 | LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_DDOT), ret = LY_EVALID, error); |
261 | |
|
262 | 0 | do { |
263 | | /* '/' */ |
264 | 0 | LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error); |
265 | | |
266 | | /* optional '..' */ |
267 | 0 | } while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_DDOT)); |
268 | 0 | } |
269 | 0 | } |
270 | 0 | } else { |
271 | | /* '/' */ |
272 | 0 | LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error); |
273 | 0 | } |
274 | | |
275 | 0 | do { |
276 | | /* NameTest */ |
277 | 0 | LY_CHECK_ERR_GOTO(lyxp_check_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), ret = LY_EVALID, error); |
278 | | |
279 | | /* check prefix based on the options */ |
280 | 0 | cur_node = exp->expr + exp->tok_pos[tok_idx]; |
281 | 0 | cur_len = exp->tok_len[tok_idx]; |
282 | 0 | if (prefix == LY_PATH_PREFIX_MANDATORY) { |
283 | 0 | if (!strnstr(cur_node, ":", cur_len)) { |
284 | 0 | LOGVAL(ctx, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", cur_len, cur_node); |
285 | 0 | ret = LY_EVALID; |
286 | 0 | goto error; |
287 | 0 | } |
288 | 0 | } else if (prefix == LY_PATH_PREFIX_STRICT_INHERIT) { |
289 | 0 | if (!prev_prefix) { |
290 | | /* the first node must have a prefix */ |
291 | 0 | if (!strnstr(cur_node, ":", cur_len)) { |
292 | 0 | LOGVAL(ctx, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", cur_len, cur_node); |
293 | 0 | ret = LY_EVALID; |
294 | 0 | goto error; |
295 | 0 | } |
296 | | |
297 | | /* remember the first prefix */ |
298 | 0 | prev_prefix = cur_node; |
299 | 0 | } else { |
300 | | /* the prefix must be different, if any */ |
301 | 0 | ptr = strnstr(cur_node, ":", cur_len); |
302 | 0 | if (ptr) { |
303 | 0 | if (!strncmp(prev_prefix, cur_node, ptr - cur_node) && (prev_prefix[ptr - cur_node] == ':')) { |
304 | 0 | LOGVAL(ctx, LYVE_XPATH, "Duplicate prefix for \"%.*s\" in path.", cur_len, cur_node); |
305 | 0 | ret = LY_EVALID; |
306 | 0 | goto error; |
307 | 0 | } |
308 | | |
309 | | /* remember this next prefix */ |
310 | 0 | prev_prefix = cur_node; |
311 | 0 | } |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | 0 | ++tok_idx; |
316 | | |
317 | | /* Predicate* */ |
318 | 0 | LY_CHECK_GOTO(ret = ly_path_check_predicate(ctx, ctx_node, exp, &tok_idx, prefix, pred), error); |
319 | | |
320 | | /* '/' */ |
321 | 0 | } while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH)); |
322 | | |
323 | | /* trailing token check */ |
324 | 0 | if (exp->used > tok_idx) { |
325 | 0 | LOGVAL(ctx, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of path.", exp->expr + exp->tok_pos[tok_idx]); |
326 | 0 | ret = LY_EVALID; |
327 | 0 | goto error; |
328 | 0 | } |
329 | | |
330 | 0 | *expr = exp; |
331 | |
|
332 | 0 | LOG_LOCBACK(ctx_node ? 1 : 0, 0, 0, 0); |
333 | 0 | return LY_SUCCESS; |
334 | | |
335 | 0 | error: |
336 | 0 | lyxp_expr_free(ctx, exp); |
337 | 0 | LOG_LOCBACK(ctx_node ? 1 : 0, 0, 0, 0); |
338 | 0 | return ret; |
339 | 0 | } |
340 | | |
341 | | LY_ERR |
342 | | ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const char *str_path, |
343 | | size_t path_len, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr) |
344 | 0 | { |
345 | 0 | LY_ERR ret = LY_SUCCESS; |
346 | 0 | struct lyxp_expr *exp = NULL; |
347 | 0 | uint16_t tok_idx; |
348 | |
|
349 | 0 | assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY)); |
350 | 0 | assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF)); |
351 | |
|
352 | 0 | LOG_LOCSET(cur_node, NULL, NULL, NULL); |
353 | | |
354 | | /* parse as a generic XPath expression */ |
355 | 0 | LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 0, &exp), error); |
356 | 0 | tok_idx = 0; |
357 | |
|
358 | 0 | LY_CHECK_GOTO(ret = ly_path_check_predicate(ctx, cur_node, exp, &tok_idx, prefix, pred), error); |
359 | | |
360 | | /* trailing token check */ |
361 | 0 | if (exp->used > tok_idx) { |
362 | 0 | LOGVAL(ctx, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of predicate.", |
363 | 0 | exp->expr + exp->tok_pos[tok_idx]); |
364 | 0 | ret = LY_EVALID; |
365 | 0 | goto error; |
366 | 0 | } |
367 | | |
368 | 0 | *expr = exp; |
369 | |
|
370 | 0 | LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0); |
371 | 0 | return LY_SUCCESS; |
372 | | |
373 | 0 | error: |
374 | 0 | lyxp_expr_free(ctx, exp); |
375 | 0 | LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0); |
376 | 0 | return ret; |
377 | 0 | } |
378 | | |
379 | | /** |
380 | | * @brief Parse prefix from a NameTest, if any, and node name, and return expected module of the node. |
381 | | * |
382 | | * @param[in] ctx libyang context. |
383 | | * @param[in] cur_node Optional current (original context) node. |
384 | | * @param[in] cur_mod Current module of the path (where the path is "instantiated"). Needed for ::LY_VALUE_SCHEMA*. |
385 | | * @param[in] prev_ctx_node Previous context node. Needed for ::LY_VALUE_JSON. |
386 | | * @param[in] expr Parsed path. |
387 | | * @param[in] tok_idx Index in @p expr. |
388 | | * @param[in] lref Lref option. |
389 | | * @param[in] format Format of the path. |
390 | | * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix). |
391 | | * @param[out] mod Resolved module. |
392 | | * @param[out] name Parsed node name. |
393 | | * @param[out] name_len Length of @p name. |
394 | | * @return LY_ERR value. |
395 | | */ |
396 | | static LY_ERR |
397 | | ly_path_compile_prefix(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod, |
398 | | const struct lysc_node *prev_ctx_node, const struct lyxp_expr *expr, uint16_t tok_idx, uint8_t lref, |
399 | | LY_VALUE_FORMAT format, void *prefix_data, struct lys_glob_unres *unres, const struct lys_module **mod, |
400 | | const char **name, size_t *name_len) |
401 | 0 | { |
402 | 0 | LY_ERR ret; |
403 | 0 | const char *pref; |
404 | 0 | size_t len; |
405 | |
|
406 | 0 | assert(expr->tokens[tok_idx] == LYXP_TOKEN_NAMETEST); |
407 | | |
408 | | /* get prefix */ |
409 | 0 | if ((pref = strnstr(expr->expr + expr->tok_pos[tok_idx], ":", expr->tok_len[tok_idx]))) { |
410 | 0 | len = pref - (expr->expr + expr->tok_pos[tok_idx]); |
411 | 0 | pref = expr->expr + expr->tok_pos[tok_idx]; |
412 | 0 | } else { |
413 | 0 | len = 0; |
414 | 0 | } |
415 | | |
416 | | /* find next node module */ |
417 | 0 | if (pref) { |
418 | 0 | LOG_LOCSET(cur_node, NULL, NULL, NULL); |
419 | |
|
420 | 0 | *mod = ly_resolve_prefix(ctx, pref, len, format, prefix_data); |
421 | 0 | if (!*mod) { |
422 | 0 | LOGVAL(ctx, LYVE_XPATH, "No module connected with the prefix \"%.*s\" found (prefix format %s).", |
423 | 0 | (int)len, pref, ly_format2str(format)); |
424 | 0 | ret = LY_EVALID; |
425 | 0 | goto error; |
426 | 0 | } else if (!(*mod)->implemented) { |
427 | 0 | if (lref == LY_PATH_LREF_FALSE) { |
428 | 0 | LOGVAL(ctx, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name); |
429 | 0 | ret = LY_EVALID; |
430 | 0 | goto error; |
431 | 0 | } |
432 | | |
433 | 0 | assert(unres); |
434 | 0 | LY_CHECK_GOTO(ret = lys_set_implemented_r((struct lys_module *)*mod, NULL, unres), error); |
435 | 0 | if (unres->recompile) { |
436 | 0 | return LY_ERECOMPILE; |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | 0 | LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0); |
441 | 0 | } else { |
442 | 0 | switch (format) { |
443 | 0 | case LY_VALUE_SCHEMA: |
444 | 0 | case LY_VALUE_SCHEMA_RESOLVED: |
445 | 0 | if (!cur_mod) { |
446 | 0 | LOGINT_RET(ctx); |
447 | 0 | } |
448 | | /* use current module */ |
449 | 0 | *mod = cur_mod; |
450 | 0 | break; |
451 | 0 | case LY_VALUE_JSON: |
452 | 0 | if (!prev_ctx_node) { |
453 | 0 | LOGINT_RET(ctx); |
454 | 0 | } |
455 | | /* inherit module of the previous node */ |
456 | 0 | *mod = prev_ctx_node->module; |
457 | 0 | break; |
458 | 0 | case LY_VALUE_CANON: |
459 | 0 | case LY_VALUE_XML: |
460 | 0 | case LY_VALUE_LYB: |
461 | | /* not really defined or accepted */ |
462 | 0 | LOGINT_RET(ctx); |
463 | 0 | } |
464 | 0 | } |
465 | | |
466 | | /* set name */ |
467 | 0 | if (pref) { |
468 | 0 | *name = pref + len + 1; |
469 | 0 | *name_len = expr->tok_len[tok_idx] - len - 1; |
470 | 0 | } else { |
471 | 0 | *name = expr->expr + expr->tok_pos[tok_idx]; |
472 | 0 | *name_len = expr->tok_len[tok_idx]; |
473 | 0 | } |
474 | |
|
475 | 0 | return LY_SUCCESS; |
476 | | |
477 | 0 | error: |
478 | 0 | LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0); |
479 | 0 | return ret; |
480 | 0 | } |
481 | | |
482 | | LY_ERR |
483 | | ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod, |
484 | | const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint16_t *tok_idx, LY_VALUE_FORMAT format, |
485 | | void *prefix_data, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type) |
486 | 0 | { |
487 | 0 | LY_ERR ret = LY_SUCCESS; |
488 | 0 | struct ly_path_predicate *p; |
489 | 0 | const struct lysc_node *key; |
490 | 0 | const struct lys_module *mod = NULL; |
491 | 0 | const char *name; |
492 | 0 | size_t name_len, key_count; |
493 | |
|
494 | 0 | assert(ctx && ctx_node); |
495 | |
|
496 | 0 | LOG_LOCSET(cur_node, NULL, NULL, NULL); |
497 | |
|
498 | 0 | *pred_type = 0; |
499 | |
|
500 | 0 | if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) { |
501 | | /* '[', no predicate */ |
502 | 0 | goto cleanup; /* LY_SUCCESS */ |
503 | 0 | } |
504 | | |
505 | 0 | if (expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST) { |
506 | 0 | if (ctx_node->nodetype != LYS_LIST) { |
507 | 0 | LOGVAL(ctx, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.", |
508 | 0 | lys_nodetype2str(ctx_node->nodetype), ctx_node->name); |
509 | 0 | ret = LY_EVALID; |
510 | 0 | goto cleanup; |
511 | 0 | } else if (ctx_node->flags & LYS_KEYLESS) { |
512 | 0 | LOGVAL(ctx, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.", |
513 | 0 | lys_nodetype2str(ctx_node->nodetype), ctx_node->name); |
514 | 0 | ret = LY_EVALID; |
515 | 0 | goto cleanup; |
516 | 0 | } |
517 | | |
518 | 0 | do { |
519 | | /* NameTest, find the key */ |
520 | 0 | LY_CHECK_RET(ly_path_compile_prefix(ctx, cur_node, cur_mod, ctx_node, expr, *tok_idx, LY_PATH_LREF_FALSE, |
521 | 0 | format, prefix_data, NULL, &mod, &name, &name_len)); |
522 | 0 | key = lys_find_child(ctx_node, mod, name, name_len, 0, 0); |
523 | 0 | if (!key) { |
524 | 0 | LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name); |
525 | 0 | ret = LY_ENOTFOUND; |
526 | 0 | goto cleanup; |
527 | 0 | } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) { |
528 | 0 | LOGVAL(ctx, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.", lys_nodetype2str(key->nodetype), |
529 | 0 | key->name); |
530 | 0 | ret = LY_EVALID; |
531 | 0 | goto cleanup; |
532 | 0 | } |
533 | 0 | ++(*tok_idx); |
534 | |
|
535 | 0 | if (!*pred_type) { |
536 | | /* new predicate */ |
537 | 0 | *pred_type = LY_PATH_PREDTYPE_LIST; |
538 | 0 | } |
539 | 0 | assert(*pred_type == LY_PATH_PREDTYPE_LIST); |
540 | 0 | LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup); |
541 | 0 | p->key = key; |
542 | | |
543 | | /* '=' */ |
544 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL); |
545 | 0 | ++(*tok_idx); |
546 | | |
547 | | /* Literal */ |
548 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL); |
549 | 0 | LOG_LOCSET(key, NULL, NULL, NULL); |
550 | 0 | ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaf *)key)->type, |
551 | 0 | expr->expr + expr->tok_pos[*tok_idx] + 1, expr->tok_len[*tok_idx] - 2, NULL, format, prefix_data, |
552 | 0 | LYD_HINT_DATA, key, NULL); |
553 | 0 | LOG_LOCBACK(key ? 1 : 0, 0, 0, 0); |
554 | 0 | LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup); |
555 | 0 | ++(*tok_idx); |
556 | | |
557 | | /* "allocate" the type to avoid problems when freeing the value after the type was freed */ |
558 | 0 | ++((struct lysc_type *)p->value.realtype)->refcount; |
559 | | |
560 | | /* ']' */ |
561 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2); |
562 | 0 | ++(*tok_idx); |
563 | | |
564 | | /* another predicate follows? */ |
565 | 0 | } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)); |
566 | | |
567 | | /* check that all keys were set */ |
568 | 0 | key_count = 0; |
569 | 0 | for (key = lysc_node_child(ctx_node); key && (key->flags & LYS_KEY); key = key->next) { |
570 | 0 | ++key_count; |
571 | 0 | } |
572 | 0 | if (LY_ARRAY_COUNT(*predicates) != key_count) { |
573 | | /* names (keys) are unique - it was checked when parsing */ |
574 | 0 | LOGVAL(ctx, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.", |
575 | 0 | lys_nodetype2str(ctx_node->nodetype), ctx_node->name); |
576 | 0 | ly_path_predicates_free(ctx, LY_PATH_PREDTYPE_LIST, *predicates); |
577 | 0 | *predicates = NULL; |
578 | 0 | ret = LY_EVALID; |
579 | 0 | goto cleanup; |
580 | 0 | } |
581 | |
|
582 | 0 | } else if (expr->tokens[*tok_idx] == LYXP_TOKEN_DOT) { |
583 | 0 | if (ctx_node->nodetype != LYS_LEAFLIST) { |
584 | 0 | LOGVAL(ctx, LYVE_XPATH, "Leaf-list predicate defined for %s \"%s\" in path.", |
585 | 0 | lys_nodetype2str(ctx_node->nodetype), ctx_node->name); |
586 | 0 | ret = LY_EVALID; |
587 | 0 | goto cleanup; |
588 | 0 | } |
589 | 0 | ++(*tok_idx); |
590 | | |
591 | | /* new predicate */ |
592 | 0 | *pred_type = LY_PATH_PREDTYPE_LEAFLIST; |
593 | 0 | LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup); |
594 | | |
595 | | /* '=' */ |
596 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL); |
597 | 0 | ++(*tok_idx); |
598 | |
|
599 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL); |
600 | | /* store the value */ |
601 | 0 | LOG_LOCSET(ctx_node, NULL, NULL, NULL); |
602 | 0 | ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type, |
603 | 0 | expr->expr + expr->tok_pos[*tok_idx] + 1, expr->tok_len[*tok_idx] - 2, NULL, format, prefix_data, |
604 | 0 | LYD_HINT_DATA, ctx_node, NULL); |
605 | 0 | LOG_LOCBACK(ctx_node ? 1 : 0, 0, 0, 0); |
606 | 0 | LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup); |
607 | 0 | ++(*tok_idx); |
608 | | |
609 | | /* "allocate" the type to avoid problems when freeing the value after the type was freed */ |
610 | 0 | ++((struct lysc_type *)p->value.realtype)->refcount; |
611 | | |
612 | | /* ']' */ |
613 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2); |
614 | 0 | ++(*tok_idx); |
615 | 0 | } else { |
616 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER); |
617 | 0 | if (!(ctx_node->nodetype & (LYS_LEAFLIST | LYS_LIST))) { |
618 | 0 | ret = LY_EVALID; |
619 | 0 | LOGVAL(ctx, LYVE_XPATH, "Positional predicate defined for %s \"%s\" in path.", |
620 | 0 | lys_nodetype2str(ctx_node->nodetype), ctx_node->name); |
621 | 0 | goto cleanup; |
622 | 0 | } else if (ctx_node->flags & LYS_CONFIG_W) { |
623 | 0 | ret = LY_EVALID; |
624 | 0 | LOGVAL(ctx, LYVE_XPATH, "Positional predicate defined for configuration %s \"%s\" in path.", |
625 | 0 | lys_nodetype2str(ctx_node->nodetype), ctx_node->name); |
626 | 0 | goto cleanup; |
627 | 0 | } |
628 | | |
629 | | /* new predicate */ |
630 | 0 | *pred_type = LY_PATH_PREDTYPE_POSITION; |
631 | 0 | LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup); |
632 | | |
633 | | /* syntax was already checked */ |
634 | 0 | p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&name, LY_BASE_DEC); |
635 | 0 | ++(*tok_idx); |
636 | | |
637 | | /* ']' */ |
638 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2); |
639 | 0 | ++(*tok_idx); |
640 | 0 | } |
641 | | |
642 | 0 | cleanup: |
643 | 0 | LOG_LOCBACK(cur_node ? 1 : 0, 0, 0, 0); |
644 | 0 | return ret; |
645 | 0 | } |
646 | | |
647 | | /** |
648 | | * @brief Compile leafref predicate. Actually, it is only checked. |
649 | | * |
650 | | * @param[in] ctx_node Context node, node for which the predicate is defined. |
651 | | * @param[in] cur_node Current (original context) node. |
652 | | * @param[in] expr Parsed path. |
653 | | * @param[in,out] tok_idx Index in @p expr, is adjusted for parsed tokens. |
654 | | * @param[in] format Format of the path. |
655 | | * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix). |
656 | | * @param[in,out] unres Global unres structure for newly implemented modules. |
657 | | * @return LY_ERR value. |
658 | | */ |
659 | | static LY_ERR |
660 | | ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct lysc_node *cur_node, |
661 | | const struct lyxp_expr *expr, uint16_t *tok_idx, LY_VALUE_FORMAT format, void *prefix_data, |
662 | | struct lys_glob_unres *unres) |
663 | 0 | { |
664 | 0 | LY_ERR ret = LY_SUCCESS; |
665 | 0 | const struct lysc_node *key, *node, *node2; |
666 | 0 | const struct lys_module *mod; |
667 | 0 | const char *name; |
668 | 0 | size_t name_len; |
669 | 0 | struct ly_ctx *ctx = cur_node->module->ctx; |
670 | |
|
671 | 0 | LOG_LOCSET(cur_node, NULL, NULL, NULL); |
672 | |
|
673 | 0 | if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) { |
674 | | /* '[', no predicate */ |
675 | 0 | goto cleanup; /* LY_SUCCESS */ |
676 | 0 | } |
677 | | |
678 | 0 | if (ctx_node->nodetype != LYS_LIST) { |
679 | 0 | LOGVAL(ctx, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.", |
680 | 0 | lys_nodetype2str(ctx_node->nodetype), ctx_node->name); |
681 | 0 | ret = LY_EVALID; |
682 | 0 | goto cleanup; |
683 | 0 | } else if (ctx_node->flags & LYS_KEYLESS) { |
684 | 0 | LOGVAL(ctx, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.", |
685 | 0 | lys_nodetype2str(ctx_node->nodetype), ctx_node->name); |
686 | 0 | ret = LY_EVALID; |
687 | 0 | goto cleanup; |
688 | 0 | } |
689 | | |
690 | 0 | do { |
691 | | /* NameTest, find the key */ |
692 | 0 | ret = ly_path_compile_prefix(ctx, cur_node, cur_node->module, ctx_node, expr, *tok_idx, |
693 | 0 | LY_PATH_LREF_TRUE, format, prefix_data, unres, &mod, &name, &name_len); |
694 | 0 | LY_CHECK_GOTO(ret, cleanup); |
695 | 0 | key = lys_find_child(ctx_node, mod, name, name_len, 0, 0); |
696 | 0 | if (!key) { |
697 | 0 | LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name); |
698 | 0 | ret = LY_EVALID; |
699 | 0 | goto cleanup; |
700 | 0 | } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) { |
701 | 0 | LOGVAL(ctx, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.", |
702 | 0 | lys_nodetype2str(key->nodetype), key->name); |
703 | 0 | ret = LY_EVALID; |
704 | 0 | goto cleanup; |
705 | 0 | } |
706 | 0 | ++(*tok_idx); |
707 | | |
708 | | /* we are not actually compiling, throw the key away */ |
709 | 0 | (void)key; |
710 | | |
711 | | /* '=' */ |
712 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL); |
713 | 0 | ++(*tok_idx); |
714 | | |
715 | | /* FuncName */ |
716 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_FUNCNAME); |
717 | 0 | ++(*tok_idx); |
718 | | |
719 | | /* evaluating from the "current()" node */ |
720 | 0 | node = cur_node; |
721 | | |
722 | | /* '(' */ |
723 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR1); |
724 | 0 | ++(*tok_idx); |
725 | | |
726 | | /* ')' */ |
727 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_PAR2); |
728 | 0 | ++(*tok_idx); |
729 | |
|
730 | 0 | do { |
731 | | /* '/' */ |
732 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH); |
733 | 0 | ++(*tok_idx); |
734 | | |
735 | | /* go to parent */ |
736 | 0 | if (!node) { |
737 | 0 | LOGVAL(ctx, LYVE_XPATH, "Too many parent references in path."); |
738 | 0 | ret = LY_EVALID; |
739 | 0 | goto cleanup; |
740 | 0 | } |
741 | 0 | node = lysc_data_parent(node); |
742 | | |
743 | | /* '..' */ |
744 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_DDOT); |
745 | 0 | ++(*tok_idx); |
746 | 0 | } while (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_DDOT); |
747 | | |
748 | 0 | do { |
749 | | /* '/' */ |
750 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH); |
751 | 0 | ++(*tok_idx); |
752 | | |
753 | | /* NameTest */ |
754 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST); |
755 | 0 | LY_CHECK_RET(ly_path_compile_prefix(ctx, cur_node, cur_node->module, node, expr, *tok_idx, |
756 | 0 | LY_PATH_LREF_TRUE, format, prefix_data, unres, &mod, &name, &name_len)); |
757 | 0 | node2 = lys_find_child(node, mod, name, name_len, 0, 0); |
758 | 0 | if (!node2) { |
759 | 0 | LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name); |
760 | 0 | ret = LY_EVALID; |
761 | 0 | goto cleanup; |
762 | 0 | } |
763 | 0 | node = node2; |
764 | 0 | ++(*tok_idx); |
765 | 0 | } while ((*tok_idx + 1 < expr->used) && (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_NAMETEST)); |
766 | | |
767 | | /* check the last target node */ |
768 | 0 | if (node->nodetype != LYS_LEAF) { |
769 | 0 | LOGVAL(ctx, LYVE_XPATH, "Leaf expected instead of %s \"%s\" in leafref predicate in path.", |
770 | 0 | lys_nodetype2str(node->nodetype), node->name); |
771 | 0 | ret = LY_EVALID; |
772 | 0 | goto cleanup; |
773 | 0 | } |
774 | | |
775 | | /* we are not actually compiling, throw the rightside node away */ |
776 | 0 | (void)node; |
777 | | |
778 | | /* ']' */ |
779 | 0 | assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2); |
780 | 0 | ++(*tok_idx); |
781 | | |
782 | | /* another predicate follows? */ |
783 | 0 | } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)); |
784 | | |
785 | 0 | cleanup: |
786 | 0 | LOG_LOCBACK(1, 0, 0, 0); |
787 | 0 | return ret; |
788 | 0 | } |
789 | | |
790 | | LY_ERR |
791 | | ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node, |
792 | | const struct lysc_ext_instance *ext, const struct lyxp_expr *expr, uint8_t lref, uint8_t oper, uint8_t target, |
793 | | LY_VALUE_FORMAT format, void *prefix_data, struct lys_glob_unres *unres, struct ly_path **path) |
794 | 0 | { |
795 | 0 | LY_ERR ret = LY_SUCCESS; |
796 | 0 | uint16_t tok_idx = 0; |
797 | 0 | const struct lys_module *mod = NULL; |
798 | 0 | const struct lysc_node *node2, *cur_node, *op; |
799 | 0 | struct ly_path *p = NULL; |
800 | 0 | uint32_t getnext_opts; |
801 | 0 | const char *name; |
802 | 0 | size_t name_len; |
803 | |
|
804 | 0 | assert(ctx); |
805 | 0 | assert((lref == LY_PATH_LREF_FALSE) || ctx_node); |
806 | 0 | assert((lref == LY_PATH_LREF_TRUE) || (lref == LY_PATH_LREF_FALSE)); |
807 | 0 | assert((oper == LY_PATH_OPER_INPUT) || (oper == LY_PATH_OPER_OUTPUT)); |
808 | 0 | assert((target == LY_PATH_TARGET_SINGLE) || (target == LY_PATH_TARGET_MANY)); |
809 | | |
810 | | /* find operation, if we are in any */ |
811 | 0 | for (op = ctx_node; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent) {} |
812 | |
|
813 | 0 | *path = NULL; |
814 | | |
815 | | /* remember original context node */ |
816 | 0 | cur_node = ctx_node; |
817 | 0 | LOG_LOCINIT(cur_node, NULL, NULL, NULL); |
818 | |
|
819 | 0 | if (oper == LY_PATH_OPER_OUTPUT) { |
820 | 0 | getnext_opts = LYS_GETNEXT_OUTPUT; |
821 | 0 | } else { |
822 | 0 | getnext_opts = 0; |
823 | 0 | } |
824 | |
|
825 | 0 | if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) { |
826 | | /* absolute path */ |
827 | 0 | ctx_node = NULL; |
828 | |
|
829 | 0 | ++tok_idx; |
830 | 0 | } else { |
831 | | /* relative path */ |
832 | 0 | while ((lref == LY_PATH_LREF_TRUE) && (expr->tokens[tok_idx] == LYXP_TOKEN_DDOT)) { |
833 | 0 | if (!ctx_node) { |
834 | 0 | LOGVAL(ctx, LYVE_XPATH, "Too many parent references in path."); |
835 | 0 | ret = LY_EVALID; |
836 | 0 | goto cleanup; |
837 | 0 | } |
838 | | |
839 | | /* get parent */ |
840 | 0 | ctx_node = lysc_data_parent(ctx_node); |
841 | |
|
842 | 0 | ++tok_idx; |
843 | |
|
844 | 0 | assert(expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH); |
845 | 0 | ++tok_idx; |
846 | 0 | } |
847 | | |
848 | | /* we are not storing the parent */ |
849 | 0 | (void)ctx_node; |
850 | 0 | } |
851 | | |
852 | 0 | do { |
853 | | /* check last compiled inner node, whether it is uniquely identified (even key-less list) */ |
854 | 0 | if (p && (lref == LY_PATH_LREF_FALSE) && (target == LY_PATH_TARGET_SINGLE) && |
855 | 0 | (p->node->nodetype == LYS_LIST) && !p->predicates) { |
856 | 0 | LOGVAL(ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.", |
857 | 0 | lys_nodetype2str(p->node->nodetype), p->node->name); |
858 | 0 | ret = LY_EVALID; |
859 | 0 | goto cleanup; |
860 | 0 | } |
861 | | |
862 | | /* NameTest */ |
863 | 0 | LY_CHECK_ERR_GOTO(lyxp_check_token(ctx, expr, tok_idx, LYXP_TOKEN_NAMETEST), ret = LY_EVALID, cleanup); |
864 | | |
865 | | /* get module and node name */ |
866 | 0 | LY_CHECK_GOTO(ret = ly_path_compile_prefix(ctx, cur_node, cur_mod, ctx_node, expr, tok_idx, lref, format, |
867 | 0 | prefix_data, unres, &mod, &name, &name_len), cleanup); |
868 | 0 | ++tok_idx; |
869 | | |
870 | | /* find the next node */ |
871 | 0 | if (!ctx_node && ext) { |
872 | 0 | node2 = lysc_ext_find_node(ext, mod, name, name_len, 0, getnext_opts); |
873 | 0 | } else { |
874 | 0 | node2 = lys_find_child(ctx_node, mod, name, name_len, 0, getnext_opts); |
875 | 0 | } |
876 | 0 | if (!node2 || (op && (node2->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (node2 != op))) { |
877 | 0 | LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name); |
878 | 0 | ret = LY_EVALID; |
879 | 0 | goto cleanup; |
880 | 0 | } |
881 | 0 | ctx_node = node2; |
882 | | |
883 | | /* new path segment */ |
884 | 0 | LY_ARRAY_NEW_GOTO(ctx, *path, p, ret, cleanup); |
885 | 0 | p->node = ctx_node; |
886 | | |
887 | | /* compile any predicates */ |
888 | 0 | if (lref == LY_PATH_LREF_TRUE) { |
889 | 0 | ret = ly_path_compile_predicate_leafref(ctx_node, cur_node, expr, &tok_idx, format, prefix_data, unres); |
890 | 0 | } else { |
891 | 0 | ret = ly_path_compile_predicate(ctx, cur_node, cur_mod, ctx_node, expr, &tok_idx, format, prefix_data, |
892 | 0 | &p->predicates, &p->pred_type); |
893 | 0 | } |
894 | 0 | LY_CHECK_GOTO(ret, cleanup); |
895 | 0 | } while (!lyxp_next_token(NULL, expr, &tok_idx, LYXP_TOKEN_OPER_PATH)); |
896 | | |
897 | | /* check leftover tokens */ |
898 | 0 | if (tok_idx < expr->used) { |
899 | 0 | LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_print_token(expr->tokens[tok_idx]), &expr->expr[expr->tok_pos[tok_idx]]); |
900 | 0 | ret = LY_EVALID; |
901 | 0 | goto cleanup; |
902 | 0 | } |
903 | | |
904 | | /* check last compiled node */ |
905 | 0 | if ((lref == LY_PATH_LREF_FALSE) && (target == LY_PATH_TARGET_SINGLE) && |
906 | 0 | (p->node->nodetype & (LYS_LIST | LYS_LEAFLIST)) && !p->predicates) { |
907 | 0 | LOGVAL(ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.", |
908 | 0 | lys_nodetype2str(p->node->nodetype), p->node->name); |
909 | 0 | ret = LY_EVALID; |
910 | 0 | goto cleanup; |
911 | 0 | } |
912 | | |
913 | 0 | cleanup: |
914 | 0 | if (ret) { |
915 | 0 | ly_path_free(ctx, *path); |
916 | 0 | *path = NULL; |
917 | 0 | } |
918 | 0 | LOG_LOCBACK(1, 0, 0, 0); |
919 | 0 | return ret; |
920 | 0 | } |
921 | | |
922 | | LY_ERR |
923 | | ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_COUNT_TYPE *path_idx, |
924 | | struct lyd_node **match) |
925 | 0 | { |
926 | 0 | LY_ARRAY_COUNT_TYPE u; |
927 | 0 | struct lyd_node *prev_node = NULL, *node = NULL, *target; |
928 | 0 | uint64_t pos; |
929 | |
|
930 | 0 | assert(path && start); |
931 | |
|
932 | 0 | if (lysc_data_parent(path[0].node)) { |
933 | | /* relative path, start from the parent children */ |
934 | 0 | start = lyd_child(start); |
935 | 0 | } else { |
936 | | /* absolute path, start from the first top-level sibling */ |
937 | 0 | while (start->parent) { |
938 | 0 | start = lyd_parent(start); |
939 | 0 | } |
940 | 0 | while (start->prev->next) { |
941 | 0 | start = start->prev; |
942 | 0 | } |
943 | 0 | } |
944 | |
|
945 | 0 | LY_ARRAY_FOR(path, u) { |
946 | 0 | switch (path[u].pred_type) { |
947 | 0 | case LY_PATH_PREDTYPE_POSITION: |
948 | | /* we cannot use hashes and want an instance on a specific position */ |
949 | 0 | pos = 1; |
950 | 0 | LYD_LIST_FOR_INST(start, path[u].node, node) { |
951 | 0 | if (pos == path[u].predicates[0].position) { |
952 | 0 | break; |
953 | 0 | } |
954 | 0 | ++pos; |
955 | 0 | } |
956 | 0 | break; |
957 | 0 | case LY_PATH_PREDTYPE_LEAFLIST: |
958 | | /* we will use hashes to find one leaf-list instance */ |
959 | 0 | LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target)); |
960 | 0 | lyd_find_sibling_first(start, target, &node); |
961 | 0 | lyd_free_tree(target); |
962 | 0 | break; |
963 | 0 | case LY_PATH_PREDTYPE_LIST: |
964 | | /* we will use hashes to find one list instance */ |
965 | 0 | LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, &target)); |
966 | 0 | lyd_find_sibling_first(start, target, &node); |
967 | 0 | lyd_free_tree(target); |
968 | 0 | break; |
969 | 0 | case LY_PATH_PREDTYPE_NONE: |
970 | | /* we will use hashes to find one any/container/leaf instance */ |
971 | 0 | lyd_find_sibling_val(start, path[u].node, NULL, 0, &node); |
972 | 0 | break; |
973 | 0 | } |
974 | | |
975 | 0 | if (!node) { |
976 | | /* no matching nodes */ |
977 | 0 | break; |
978 | 0 | } |
979 | | |
980 | | /* rememeber previous node */ |
981 | 0 | prev_node = node; |
982 | | |
983 | | /* next path segment, if any */ |
984 | 0 | start = lyd_child(node); |
985 | 0 | } |
986 | | |
987 | 0 | if (node) { |
988 | | /* we have found the full path */ |
989 | 0 | if (path_idx) { |
990 | 0 | *path_idx = u; |
991 | 0 | } |
992 | 0 | if (match) { |
993 | 0 | *match = node; |
994 | 0 | } |
995 | 0 | return LY_SUCCESS; |
996 | |
|
997 | 0 | } else if (prev_node) { |
998 | | /* we have found only some partial match */ |
999 | 0 | if (path_idx) { |
1000 | 0 | *path_idx = u - 1; |
1001 | 0 | } |
1002 | 0 | if (match) { |
1003 | 0 | *match = prev_node; |
1004 | 0 | } |
1005 | 0 | return LY_EINCOMPLETE; |
1006 | 0 | } |
1007 | | |
1008 | | /* we have not found any nodes */ |
1009 | 0 | if (path_idx) { |
1010 | 0 | *path_idx = 0; |
1011 | 0 | } |
1012 | 0 | if (match) { |
1013 | 0 | *match = NULL; |
1014 | 0 | } |
1015 | 0 | return LY_ENOTFOUND; |
1016 | 0 | } |
1017 | | |
1018 | | LY_ERR |
1019 | | ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match) |
1020 | 0 | { |
1021 | 0 | LY_ERR ret; |
1022 | 0 | struct lyd_node *m; |
1023 | |
|
1024 | 0 | ret = ly_path_eval_partial(path, start, NULL, &m); |
1025 | |
|
1026 | 0 | if (ret == LY_SUCCESS) { |
1027 | | /* last node was found */ |
1028 | 0 | if (match) { |
1029 | 0 | *match = m; |
1030 | 0 | } |
1031 | 0 | return LY_SUCCESS; |
1032 | 0 | } |
1033 | | |
1034 | | /* not a full match */ |
1035 | 0 | if (match) { |
1036 | 0 | *match = NULL; |
1037 | 0 | } |
1038 | 0 | return LY_ENOTFOUND; |
1039 | 0 | } |
1040 | | |
1041 | | LY_ERR |
1042 | | ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup) |
1043 | 0 | { |
1044 | 0 | LY_ARRAY_COUNT_TYPE u, v; |
1045 | |
|
1046 | 0 | if (!path) { |
1047 | 0 | return LY_SUCCESS; |
1048 | 0 | } |
1049 | | |
1050 | 0 | LY_ARRAY_CREATE_RET(ctx, *dup, LY_ARRAY_COUNT(path), LY_EMEM); |
1051 | 0 | LY_ARRAY_FOR(path, u) { |
1052 | 0 | LY_ARRAY_INCREMENT(*dup); |
1053 | 0 | (*dup)[u].node = path[u].node; |
1054 | 0 | if (path[u].predicates) { |
1055 | 0 | LY_ARRAY_CREATE_RET(ctx, (*dup)[u].predicates, LY_ARRAY_COUNT(path[u].predicates), LY_EMEM); |
1056 | 0 | (*dup)[u].pred_type = path[u].pred_type; |
1057 | 0 | LY_ARRAY_FOR(path[u].predicates, v) { |
1058 | 0 | struct ly_path_predicate *pred = &path[u].predicates[v]; |
1059 | |
|
1060 | 0 | LY_ARRAY_INCREMENT((*dup)[u].predicates); |
1061 | 0 | switch (path[u].pred_type) { |
1062 | 0 | case LY_PATH_PREDTYPE_POSITION: |
1063 | | /* position-predicate */ |
1064 | 0 | (*dup)[u].predicates[v].position = pred->position; |
1065 | 0 | break; |
1066 | 0 | case LY_PATH_PREDTYPE_LIST: |
1067 | 0 | case LY_PATH_PREDTYPE_LEAFLIST: |
1068 | | /* key-predicate or leaf-list-predicate */ |
1069 | 0 | (*dup)[u].predicates[v].key = pred->key; |
1070 | 0 | pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].predicates[v].value); |
1071 | 0 | ++((struct lysc_type *)pred->value.realtype)->refcount; |
1072 | 0 | break; |
1073 | 0 | case LY_PATH_PREDTYPE_NONE: |
1074 | 0 | break; |
1075 | 0 | } |
1076 | 0 | } |
1077 | 0 | } |
1078 | 0 | } |
1079 | | |
1080 | 0 | return LY_SUCCESS; |
1081 | 0 | } |
1082 | | |
1083 | | void |
1084 | | ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type, struct ly_path_predicate *predicates) |
1085 | 0 | { |
1086 | 0 | LY_ARRAY_COUNT_TYPE u; |
1087 | |
|
1088 | 0 | if (!predicates) { |
1089 | 0 | return; |
1090 | 0 | } |
1091 | | |
1092 | 0 | LY_ARRAY_FOR(predicates, u) { |
1093 | 0 | switch (pred_type) { |
1094 | 0 | case LY_PATH_PREDTYPE_POSITION: |
1095 | 0 | case LY_PATH_PREDTYPE_NONE: |
1096 | | /* nothing to free */ |
1097 | 0 | break; |
1098 | 0 | case LY_PATH_PREDTYPE_LIST: |
1099 | 0 | case LY_PATH_PREDTYPE_LEAFLIST: |
1100 | 0 | if (predicates[u].value.realtype) { |
1101 | 0 | predicates[u].value.realtype->plugin->free(ctx, &predicates[u].value); |
1102 | 0 | lysc_type_free((struct ly_ctx *)ctx, (struct lysc_type *)predicates[u].value.realtype); |
1103 | 0 | } |
1104 | 0 | break; |
1105 | 0 | } |
1106 | 0 | } |
1107 | 0 | LY_ARRAY_FREE(predicates); |
1108 | 0 | } |
1109 | | |
1110 | | void |
1111 | | ly_path_free(const struct ly_ctx *ctx, struct ly_path *path) |
1112 | 0 | { |
1113 | 0 | LY_ARRAY_COUNT_TYPE u; |
1114 | |
|
1115 | 0 | if (!path) { |
1116 | 0 | return; |
1117 | 0 | } |
1118 | | |
1119 | 0 | LY_ARRAY_FOR(path, u) { |
1120 | 0 | ly_path_predicates_free(ctx, path[u].pred_type, path[u].predicates); |
1121 | 0 | } |
1122 | 0 | LY_ARRAY_FREE(path); |
1123 | 0 | } |