/src/libyang/src/tree_data.c
Line | Count | Source |
1 | | /** |
2 | | * @file tree_data.c |
3 | | * @author Radek Krejci <rkrejci@cesnet.cz> |
4 | | * @author Michal Vasko <mvasko@cesnet.cz> |
5 | | * @brief Data tree functions |
6 | | * |
7 | | * Copyright (c) 2015 - 2022 CESNET, z.s.p.o. |
8 | | * |
9 | | * This source code is licensed under BSD 3-Clause License (the "License"). |
10 | | * You may not use this file except in compliance with the License. |
11 | | * You may obtain a copy of the License at |
12 | | * |
13 | | * https://opensource.org/licenses/BSD-3-Clause |
14 | | */ |
15 | | |
16 | | #define _GNU_SOURCE |
17 | | |
18 | | #include "tree_data.h" |
19 | | |
20 | | #include <assert.h> |
21 | | #include <ctype.h> |
22 | | #include <inttypes.h> |
23 | | #include <stdarg.h> |
24 | | #include <stdint.h> |
25 | | #include <stdio.h> |
26 | | #include <stdlib.h> |
27 | | #include <string.h> |
28 | | |
29 | | #include "compat.h" |
30 | | #include "context.h" |
31 | | #include "dict.h" |
32 | | #include "diff.h" |
33 | | #include "hash_table.h" |
34 | | #include "in.h" |
35 | | #include "in_internal.h" |
36 | | #include "log.h" |
37 | | #include "ly_common.h" |
38 | | #include "parser_data.h" |
39 | | #include "parser_internal.h" |
40 | | #include "path.h" |
41 | | #include "plugins.h" |
42 | | #include "plugins_exts/metadata.h" |
43 | | #include "plugins_internal.h" |
44 | | #include "plugins_types.h" |
45 | | #include "set.h" |
46 | | #include "tree.h" |
47 | | #include "tree_data_internal.h" |
48 | | #include "tree_data_sorted.h" |
49 | | #include "tree_edit.h" |
50 | | #include "tree_schema.h" |
51 | | #include "tree_schema_internal.h" |
52 | | #include "validation.h" |
53 | | #include "xml.h" |
54 | | #include "xpath.h" |
55 | | |
56 | | static int lyd_insert_has_keys(const struct lyd_node *list); |
57 | | |
58 | | static LY_ERR lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options, |
59 | | ly_bool parental_schemas_checked); |
60 | | |
61 | | static LYD_FORMAT |
62 | | lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format) |
63 | 0 | { |
64 | 0 | if (!format && (in->type == LY_IN_FILEPATH)) { |
65 | | /* unknown format - try to detect it from filename's suffix */ |
66 | 0 | const char *path = in->method.fpath.filepath; |
67 | 0 | size_t len = strlen(path); |
68 | | |
69 | | /* ignore trailing whitespaces */ |
70 | 0 | for ( ; len > 0 && isspace(path[len - 1]); len--) {} |
71 | |
|
72 | 0 | if ((len >= LY_XML_SUFFIX_LEN + 1) && |
73 | 0 | !strncmp(&path[len - LY_XML_SUFFIX_LEN], LY_XML_SUFFIX, LY_XML_SUFFIX_LEN)) { |
74 | 0 | format = LYD_XML; |
75 | 0 | } else if ((len >= LY_JSON_SUFFIX_LEN + 1) && |
76 | 0 | !strncmp(&path[len - LY_JSON_SUFFIX_LEN], LY_JSON_SUFFIX, LY_JSON_SUFFIX_LEN)) { |
77 | 0 | format = LYD_JSON; |
78 | 0 | } else if ((len >= LY_LYB_SUFFIX_LEN + 1) && |
79 | 0 | !strncmp(&path[len - LY_LYB_SUFFIX_LEN], LY_LYB_SUFFIX, LY_LYB_SUFFIX_LEN)) { |
80 | 0 | format = LYD_LYB; |
81 | 0 | } else if ((len >= LY_CBOR_SUFFIX_LEN + 1) && |
82 | 0 | !strncmp(&path[len - LY_CBOR_SUFFIX_LEN], LY_CBOR_SUFFIX, LY_CBOR_SUFFIX_LEN)) { |
83 | 0 | format = LYD_CBOR; |
84 | 0 | } /* else still unknown */ |
85 | 0 | } |
86 | |
|
87 | 0 | return format; |
88 | 0 | } |
89 | | |
90 | | /** |
91 | | * @brief Parse YANG data into a data tree. |
92 | | * |
93 | | * @param[in] ctx libyang context. |
94 | | * @param[in] parent Parent to connect the parsed nodes to, if any. |
95 | | * @param[in,out] first_p Pointer to the first parsed node. |
96 | | * @param[in] in Input handle to read the input from. |
97 | | * @param[in] format Expected format of the data in @p in. |
98 | | * @param[in] parse_opts Options for parser. |
99 | | * @param[in] val_opts Options for validation. |
100 | | * @param[out] op Optional pointer to the parsed operation, if any. |
101 | | * @return LY_ERR value. |
102 | | */ |
103 | | static LY_ERR |
104 | | lyd_parse(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in, |
105 | | LYD_FORMAT format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **op) |
106 | 0 | { |
107 | 0 | LY_ERR r = LY_SUCCESS, rc = LY_SUCCESS; |
108 | 0 | struct lyd_ctx *lydctx = NULL; |
109 | 0 | struct ly_set parsed = {0}; |
110 | 0 | uint32_t i, int_opts = 0; |
111 | 0 | const struct ly_err_item *eitem; |
112 | |
|
113 | 0 | assert(ctx && (parent || first_p)); |
114 | | |
115 | 0 | format = lyd_parse_get_format(in, format); |
116 | 0 | if (first_p) { |
117 | 0 | *first_p = NULL; |
118 | 0 | } |
119 | | |
120 | | /* remember input position */ |
121 | 0 | in->func_start = in->current; |
122 | | |
123 | | /* set internal options */ |
124 | 0 | int_opts = LYD_INTOPT_WITH_SIBLINGS; |
125 | | |
126 | | /* parse the data */ |
127 | 0 | switch (format) { |
128 | 0 | case LYD_XML: |
129 | 0 | r = lyd_parse_xml(ctx, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, &lydctx); |
130 | 0 | break; |
131 | 0 | case LYD_JSON: |
132 | 0 | r = lyd_parse_json(ctx, parent, NULL, first_p, in, parse_opts, val_opts, int_opts, &parsed, &lydctx); |
133 | 0 | break; |
134 | 0 | case LYD_LYB: |
135 | 0 | r = lyd_parse_lyb(ctx, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, &lydctx); |
136 | 0 | break; |
137 | | #ifdef ENABLE_CBOR_SUPPORT |
138 | | case LYD_CBOR: |
139 | | r = lyd_parse_cbor(ctx, NULL, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); |
140 | | break; |
141 | | #else |
142 | 0 | case LYD_CBOR: |
143 | 0 | LOGARG(ctx, format); |
144 | 0 | r = LY_EINVAL; |
145 | 0 | break; |
146 | 0 | #endif /* ENABLE_CBOR_SUPPORT */ |
147 | 0 | case LYD_UNKNOWN: |
148 | 0 | LOGARG(ctx, format); |
149 | 0 | r = LY_EINVAL; |
150 | 0 | break; |
151 | 0 | } |
152 | 0 | if (r) { |
153 | 0 | rc = r; |
154 | 0 | if ((r != LY_EVALID) || !lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) { |
155 | 0 | goto cleanup; |
156 | 0 | } |
157 | | |
158 | 0 | eitem = ly_err_last(ctx); |
159 | 0 | assert(eitem); |
160 | 0 | if (eitem->vecode == LYVE_SYNTAX) { |
161 | | /* cannot get more errors on a syntax error */ |
162 | 0 | goto cleanup; |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | 0 | if (parent && parsed.count) { |
167 | | /* use the first parsed node */ |
168 | 0 | if (first_p) { |
169 | 0 | *first_p = parsed.dnodes[0]; |
170 | 0 | } else { |
171 | 0 | first_p = &parsed.dnodes[0]; |
172 | 0 | } |
173 | 0 | } |
174 | |
|
175 | 0 | if (!(parse_opts & LYD_PARSE_ONLY)) { |
176 | | /* validate data */ |
177 | 0 | r = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types, |
178 | 0 | &lydctx->ext_val, NULL); |
179 | 0 | LY_CHECK_ERR_GOTO(r, rc = r, cleanup); |
180 | 0 | } |
181 | | |
182 | | /* set the operation node */ |
183 | 0 | if (op) { |
184 | 0 | *op = lydctx->op_node; |
185 | 0 | } |
186 | |
|
187 | 0 | cleanup: |
188 | 0 | if (lydctx) { |
189 | 0 | lydctx->free(lydctx); |
190 | 0 | } |
191 | 0 | if (rc) { |
192 | 0 | if (parent) { |
193 | | /* free all the parsed subtrees */ |
194 | 0 | for (i = 0; i < parsed.count; ++i) { |
195 | 0 | lyd_free_tree(parsed.dnodes[i]); |
196 | 0 | } |
197 | 0 | } else { |
198 | | /* free everything */ |
199 | 0 | lyd_free_all(*first_p); |
200 | 0 | *first_p = NULL; |
201 | 0 | } |
202 | 0 | } |
203 | 0 | ly_set_erase(&parsed, NULL); |
204 | 0 | return rc; |
205 | 0 | } |
206 | | |
207 | | LIBYANG_API_DEF LY_ERR |
208 | | lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format, |
209 | | uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) |
210 | 0 | { |
211 | 0 | LY_CHECK_ARG_RET(ctx, ctx || parent, in, parent || tree, LY_EINVAL); |
212 | 0 | LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL); |
213 | 0 | LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL); |
214 | |
|
215 | 0 | if (!ctx) { |
216 | 0 | ctx = LYD_CTX(parent); |
217 | 0 | } |
218 | |
|
219 | 0 | return lyd_parse(ctx, parent, tree, in, format, parse_options, validate_options, NULL); |
220 | 0 | } |
221 | | |
222 | | LIBYANG_API_DEF LY_ERR |
223 | | lyd_parse_data_mem_len(const struct ly_ctx *ctx, const char *data, size_t data_len, LYD_FORMAT format, |
224 | | uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) |
225 | 0 | { |
226 | 0 | LY_ERR ret; |
227 | 0 | struct ly_in *in; |
228 | |
|
229 | 0 | LY_CHECK_RET(ly_in_new_memory(data, &in)); |
230 | 0 | in->length = data_len; // Set the length for the input |
231 | |
|
232 | 0 | ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); |
233 | |
|
234 | 0 | ly_in_free(in, 0); |
235 | 0 | return ret; |
236 | 0 | } |
237 | | |
238 | | LIBYANG_API_DEF LY_ERR |
239 | | lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options, |
240 | | uint32_t validate_options, struct lyd_node **tree) |
241 | 0 | { |
242 | 0 | LY_ERR ret; |
243 | 0 | struct ly_in *in; |
244 | |
|
245 | 0 | LY_CHECK_RET(ly_in_new_memory(data, &in)); |
246 | 0 | ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); |
247 | |
|
248 | 0 | ly_in_free(in, 0); |
249 | 0 | return ret; |
250 | 0 | } |
251 | | |
252 | | LIBYANG_API_DEF LY_ERR |
253 | | lyd_parse_data_fd(const struct ly_ctx *ctx, int fd, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options, |
254 | | struct lyd_node **tree) |
255 | 0 | { |
256 | 0 | LY_ERR ret; |
257 | 0 | struct ly_in *in; |
258 | |
|
259 | 0 | LY_CHECK_RET(ly_in_new_fd(fd, &in)); |
260 | 0 | ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); |
261 | |
|
262 | 0 | ly_in_free(in, 0); |
263 | 0 | return ret; |
264 | 0 | } |
265 | | |
266 | | LIBYANG_API_DEF LY_ERR |
267 | | lyd_parse_data_path(const struct ly_ctx *ctx, const char *path, LYD_FORMAT format, uint32_t parse_options, |
268 | | uint32_t validate_options, struct lyd_node **tree) |
269 | 0 | { |
270 | 0 | LY_ERR ret; |
271 | 0 | struct ly_in *in; |
272 | |
|
273 | 0 | LY_CHECK_RET(ly_in_new_filepath(path, 0, &in)); |
274 | 0 | ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); |
275 | |
|
276 | 0 | ly_in_free(in, 0); |
277 | 0 | return ret; |
278 | 0 | } |
279 | | |
280 | | LIBYANG_API_DEF LY_ERR |
281 | | lyd_parse_value_fragment(const struct ly_ctx *ctx, const char *path, struct ly_in *in, LYD_FORMAT format, |
282 | | uint32_t new_val_options, uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) |
283 | 0 | { |
284 | 0 | LY_ERR ret = LY_SUCCESS; |
285 | 0 | struct lyxp_expr *exp = NULL; |
286 | 0 | struct ly_path *p = NULL; |
287 | 0 | struct lyd_node *new_last_parent = NULL, *new_top_parent = NULL; |
288 | 0 | const struct lysc_node *new_node_schema = NULL; |
289 | 0 | ly_bool p_decremented = 0; |
290 | 0 | struct lyd_node *iter = NULL, *parent = NULL; |
291 | 0 | const char *key_value = NULL; |
292 | 0 | const char *path_key_value = NULL; |
293 | |
|
294 | 0 | LY_CHECK_ARG_RET(ctx, ctx, path && (path[0] == '/'), LY_EINVAL); |
295 | | |
296 | | /* other formats are not supported for now */ |
297 | 0 | if (format != LYD_JSON) { |
298 | 0 | LOGARG(ctx, "invalid format (only JSON supported)"); |
299 | 0 | return LY_EINVAL; |
300 | 0 | } |
301 | | |
302 | | /* parse path */ |
303 | 0 | LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, |
304 | 0 | LY_PATH_PRED_SIMPLE, &exp), cleanup); |
305 | | |
306 | | /* compile path */ |
307 | 0 | LY_CHECK_GOTO(ret = ly_path_compile(ctx, NULL, exp, new_val_options & LYD_NEW_VAL_OUTPUT ? |
308 | 0 | LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p), cleanup); |
309 | | |
310 | | /* has to have a schema */ |
311 | 0 | new_node_schema = p[LY_ARRAY_COUNT(p) - 1].node; |
312 | | |
313 | | /* only the term nodes get their path shortened */ |
314 | 0 | if (new_node_schema->nodetype & LYD_NODE_TERM) { |
315 | | /* shorten the ly_path by one element (to avoid a leaflist without predicate at the end) */ |
316 | 0 | LY_ARRAY_DECREMENT(p); |
317 | 0 | p_decremented = 1; |
318 | 0 | } |
319 | |
|
320 | 0 | if (LY_ARRAY_COUNT(p)) { |
321 | | /* create nodes */ |
322 | 0 | LY_CHECK_GOTO(ret = lyd_new_path_create(NULL, ctx, p, path, NULL, 0, 0, new_val_options, &new_top_parent, |
323 | 0 | &new_last_parent), cleanup); |
324 | 0 | } |
325 | | |
326 | | /* parse the json value */ |
327 | 0 | LY_CHECK_GOTO(ret = lyd_parse_json(ctx, new_last_parent, new_node_schema, new_last_parent ? NULL : &new_top_parent, |
328 | 0 | in, parse_options, validate_options, 0, NULL, NULL), cleanup); |
329 | | |
330 | | /* when setting keys they have to have a correct value (same as in the path) */ |
331 | 0 | if (lysc_is_key(new_node_schema)) { |
332 | 0 | LY_LIST_FOR(lyd_child(new_last_parent), iter) { |
333 | | /* look for the same schema as is in the path */ |
334 | 0 | if (!strcmp(iter->schema->name, new_node_schema->name)) { |
335 | 0 | key_value = lyd_get_value(iter); |
336 | 0 | if (!path_key_value) { |
337 | 0 | path_key_value = key_value; |
338 | 0 | } else { |
339 | 0 | if (strcmp(key_value, path_key_value)) { |
340 | 0 | LOGVAL(ctx, NULL, LYVE_DATA, "Path [%s] contains a different key [%s] than data [%s]", path, |
341 | 0 | path_key_value, key_value); |
342 | 0 | ret = LY_EINVAL; |
343 | 0 | goto cleanup; |
344 | 0 | } |
345 | | |
346 | | /* when unlinking duplicate key we need to recalculate the list hash, |
347 | | so store the list (parent) and recalculate the hash after unlinking the duplicate key */ |
348 | 0 | parent = iter->parent; |
349 | 0 | assert(parent); |
350 | | |
351 | 0 | lyd_unlink(iter); |
352 | 0 | lyd_free_tree(iter); |
353 | |
|
354 | 0 | lyd_unlink_hash(parent); |
355 | 0 | lyd_hash(parent); |
356 | 0 | lyd_insert_hash(parent); |
357 | 0 | break; |
358 | 0 | } |
359 | 0 | } |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | | /* set output tree */ |
364 | 0 | if (tree) { |
365 | 0 | *tree = new_top_parent; |
366 | 0 | } |
367 | |
|
368 | 0 | cleanup: |
369 | 0 | lyxp_expr_free(exp); |
370 | 0 | if (p_decremented) { |
371 | 0 | LY_ARRAY_INCREMENT(p); |
372 | 0 | } |
373 | 0 | ly_path_free(p); |
374 | 0 | if (ret) { |
375 | 0 | lyd_free_all(new_top_parent); |
376 | 0 | } |
377 | 0 | return ret; |
378 | 0 | } |
379 | | |
380 | | LIBYANG_API_DEF LY_ERR |
381 | | lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format, |
382 | | enum lyd_type data_type, uint32_t parse_options, struct lyd_node **tree, struct lyd_node **op) |
383 | 0 | { |
384 | 0 | LY_ERR rc = LY_SUCCESS; |
385 | 0 | struct lyd_ctx *lydctx = NULL; |
386 | 0 | struct ly_set parsed = {0}; |
387 | 0 | struct lyd_node *first = NULL, *envp = NULL; |
388 | 0 | uint32_t i, val_opts, int_opts = 0; |
389 | 0 | ly_bool proto_msg = 0; |
390 | |
|
391 | 0 | LY_CHECK_ARG_RET(ctx, ctx || parent, in, !(parse_options & ~(LYD_PARSE_STRICT | LYD_PARSE_OPAQ)), data_type, |
392 | 0 | parent || tree || op, LY_EINVAL); |
393 | |
|
394 | 0 | parse_options |= LYD_PARSE_ONLY; |
395 | |
|
396 | 0 | if (!ctx) { |
397 | 0 | ctx = LYD_CTX(parent); |
398 | 0 | } |
399 | 0 | if (tree) { |
400 | 0 | *tree = NULL; |
401 | 0 | } |
402 | 0 | if (op) { |
403 | 0 | *op = NULL; |
404 | 0 | } |
405 | |
|
406 | 0 | format = lyd_parse_get_format(in, format); |
407 | | |
408 | | /* remember input position */ |
409 | 0 | in->func_start = in->current; |
410 | | |
411 | | /* set validation opts */ |
412 | 0 | val_opts = 0; |
413 | |
|
414 | 0 | switch (data_type) { |
415 | 0 | case LYD_TYPE_RPC_NETCONF: |
416 | 0 | case LYD_TYPE_NOTIF_NETCONF: |
417 | 0 | LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL); |
418 | 0 | proto_msg = 1; |
419 | 0 | break; |
420 | 0 | case LYD_TYPE_REPLY_NETCONF: |
421 | 0 | LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), |
422 | 0 | tree, !op, LY_EINVAL); |
423 | 0 | proto_msg = 1; |
424 | 0 | break; |
425 | 0 | case LYD_TYPE_RPC_RESTCONF: |
426 | 0 | case LYD_TYPE_REPLY_RESTCONF: |
427 | 0 | LY_CHECK_ARG_RET(ctx, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op, LY_EINVAL); |
428 | 0 | proto_msg = 1; |
429 | 0 | break; |
430 | 0 | case LYD_TYPE_NOTIF_RESTCONF: |
431 | 0 | LY_CHECK_ARG_RET(ctx, format == LYD_JSON, !parent, tree, op, LY_EINVAL); |
432 | 0 | proto_msg = 1; |
433 | 0 | break; |
434 | | |
435 | | /* set internal opts */ |
436 | 0 | case LYD_TYPE_RPC_YANG: |
437 | 0 | int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS); |
438 | 0 | break; |
439 | 0 | case LYD_TYPE_NOTIF_YANG: |
440 | 0 | int_opts = LYD_INTOPT_NOTIF | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS); |
441 | 0 | break; |
442 | 0 | case LYD_TYPE_REPLY_YANG: |
443 | 0 | int_opts = LYD_INTOPT_REPLY | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS); |
444 | 0 | break; |
445 | 0 | default: |
446 | 0 | LOGINT(ctx); |
447 | 0 | rc = LY_EINT; |
448 | 0 | goto cleanup; |
449 | 0 | } |
450 | | |
451 | | /* parse a full protocol message */ |
452 | 0 | if (proto_msg) { |
453 | 0 | if (format == LYD_XML) { |
454 | | /* parse the NETCONF (or RESTCONF XML) message */ |
455 | 0 | rc = lyd_parse_xml_netconf(ctx, parent, &first, in, parse_options, val_opts, data_type, &envp, &parsed, &lydctx); |
456 | 0 | } else { |
457 | | /* parse the RESTCONF message */ |
458 | 0 | rc = lyd_parse_json_restconf(ctx, parent, &first, in, parse_options, val_opts, data_type, &envp, &parsed, &lydctx); |
459 | 0 | } |
460 | 0 | if (rc) { |
461 | 0 | if (envp) { |
462 | | /* special situation when the envelopes were parsed successfully */ |
463 | 0 | *tree = envp; |
464 | 0 | } |
465 | 0 | goto cleanup; |
466 | 0 | } |
467 | | |
468 | | /* set out params correctly */ |
469 | 0 | if (envp) { |
470 | | /* special out param meaning */ |
471 | 0 | *tree = envp; |
472 | 0 | } else { |
473 | 0 | *tree = parent ? NULL : first; |
474 | 0 | } |
475 | 0 | if (op) { |
476 | 0 | *op = lydctx->op_node; |
477 | 0 | } |
478 | 0 | goto cleanup; |
479 | 0 | } |
480 | | |
481 | | /* parse the data */ |
482 | 0 | switch (format) { |
483 | 0 | case LYD_XML: |
484 | 0 | rc = lyd_parse_xml(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); |
485 | 0 | break; |
486 | 0 | case LYD_JSON: |
487 | 0 | rc = lyd_parse_json(ctx, parent, NULL, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); |
488 | 0 | break; |
489 | 0 | case LYD_LYB: |
490 | 0 | rc = lyd_parse_lyb(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); |
491 | 0 | break; |
492 | | #ifdef ENABLE_CBOR_SUPPORT |
493 | | case LYD_CBOR: |
494 | | rc = lyd_parse_cbor(ctx, NULL, parent, &first, in, parse_options, val_opts, int_opts, &parsed, NULL, &lydctx); |
495 | | break; |
496 | | #else |
497 | 0 | case LYD_CBOR: |
498 | 0 | LOGARG(ctx, format); |
499 | 0 | rc = LY_EINVAL; |
500 | 0 | break; |
501 | 0 | #endif /* ENABLE_CBOR_SUPPORT */ |
502 | 0 | case LYD_UNKNOWN: |
503 | 0 | LOGARG(ctx, format); |
504 | 0 | rc = LY_EINVAL; |
505 | 0 | break; |
506 | 0 | } |
507 | 0 | LY_CHECK_GOTO(rc, cleanup); |
508 | | |
509 | | /* set out params correctly */ |
510 | 0 | if (tree) { |
511 | 0 | *tree = parent ? NULL : first; |
512 | 0 | } |
513 | 0 | if (op) { |
514 | 0 | *op = lydctx->op_node; |
515 | 0 | } |
516 | |
|
517 | 0 | cleanup: |
518 | 0 | if (lydctx) { |
519 | 0 | lydctx->free(lydctx); |
520 | 0 | } |
521 | 0 | if (rc) { |
522 | | /* free all the parsed nodes */ |
523 | 0 | if (parsed.count) { |
524 | 0 | i = parsed.count; |
525 | 0 | do { |
526 | 0 | --i; |
527 | 0 | lyd_free_tree(parsed.dnodes[i]); |
528 | 0 | } while (i); |
529 | 0 | } |
530 | 0 | if (tree && !envp) { |
531 | 0 | *tree = NULL; |
532 | 0 | } |
533 | 0 | if (op) { |
534 | 0 | *op = NULL; |
535 | 0 | } |
536 | 0 | } |
537 | 0 | ly_set_erase(&parsed, NULL); |
538 | 0 | return rc; |
539 | 0 | } |
540 | | |
541 | | struct lyd_node * |
542 | | lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct lyd_node *new_node) |
543 | 0 | { |
544 | 0 | const struct lysc_node *schema, *sparent; |
545 | 0 | struct lyd_node *match = NULL; |
546 | 0 | ly_bool found; |
547 | 0 | uint32_t getnext_opts; |
548 | |
|
549 | 0 | assert(new_node); |
550 | | |
551 | 0 | if (!first_sibling || !new_node->schema || (new_node->flags & LYD_EXT)) { |
552 | | /* insert at the end, no next anchor */ |
553 | 0 | return NULL; |
554 | 0 | } |
555 | | |
556 | 0 | getnext_opts = 0; |
557 | 0 | if (new_node->schema->flags & LYS_IS_OUTPUT) { |
558 | 0 | getnext_opts = LYS_GETNEXT_OUTPUT; |
559 | 0 | } |
560 | |
|
561 | 0 | if ((LYD_CTX(first_sibling) == LYD_CTX(new_node)) && first_sibling->parent && first_sibling->parent->schema && |
562 | 0 | ((struct lyd_node_inner *)first_sibling->parent)->children_ht) { |
563 | | /* find the anchor using hashes */ |
564 | 0 | sparent = first_sibling->parent->schema; |
565 | 0 | schema = lys_getnext(new_node->schema, sparent, NULL, getnext_opts); |
566 | 0 | while (schema) { |
567 | | /* keep trying to find the first existing instance of the closest following schema sibling, |
568 | | * otherwise return NULL - inserting at the end */ |
569 | 0 | if (!lyd_find_sibling_schema(first_sibling, schema, &match)) { |
570 | 0 | break; |
571 | 0 | } |
572 | | |
573 | 0 | schema = lys_getnext(schema, sparent, NULL, getnext_opts); |
574 | 0 | } |
575 | 0 | } else { |
576 | | /* find the anchor without hashes */ |
577 | 0 | match = (struct lyd_node *)first_sibling; |
578 | 0 | sparent = lysc_data_parent(new_node->schema); |
579 | 0 | if (!sparent) { |
580 | | /* we are in top-level, skip all the data from preceding modules */ |
581 | 0 | LY_LIST_FOR(match, match) { |
582 | 0 | if (!match->schema || (strcmp(lyd_owner_module(match)->name, lyd_owner_module(new_node)->name) >= 0)) { |
583 | 0 | break; |
584 | 0 | } |
585 | 0 | } |
586 | 0 | } |
587 | | |
588 | | /* get the first schema sibling */ |
589 | 0 | schema = lys_getnext(NULL, sparent, new_node->schema->module->compiled, getnext_opts); |
590 | 0 | if (!schema) { |
591 | | /* must be a top-level extension instance data, no anchor */ |
592 | 0 | return NULL; |
593 | 0 | } |
594 | | |
595 | 0 | found = 0; |
596 | 0 | LY_LIST_FOR(match, match) { |
597 | 0 | if (!match->schema || (lyd_owner_module(match) != lyd_owner_module(new_node))) { |
598 | | /* we have found an opaque node, which must be at the end, so use it OR |
599 | | * modules do not match, so we must have traversed all the data from new_node module (if any), |
600 | | * we have found the first node of the next module, that is what we want */ |
601 | 0 | break; |
602 | 0 | } |
603 | | |
604 | | /* skip schema nodes until we find the instantiated one */ |
605 | 0 | while (!found) { |
606 | 0 | if (new_node->schema == schema) { |
607 | | /* we have found the schema of the new node, continue search to find the first |
608 | | * data node with a different schema (after our schema) */ |
609 | 0 | found = 1; |
610 | 0 | break; |
611 | 0 | } |
612 | 0 | if (match->schema == schema) { |
613 | | /* current node (match) is a data node still before the new node, continue search in data */ |
614 | 0 | break; |
615 | 0 | } |
616 | | |
617 | 0 | schema = lys_getnext(schema, sparent, new_node->schema->module->compiled, getnext_opts); |
618 | 0 | if (!schema) { |
619 | | /* must be a top-level extension instance data, no anchor */ |
620 | 0 | return NULL; |
621 | 0 | } |
622 | 0 | } |
623 | | |
624 | 0 | if (found && (match->schema != new_node->schema)) { |
625 | | /* find the next node after we have found our node schema data instance */ |
626 | 0 | break; |
627 | 0 | } |
628 | 0 | } |
629 | 0 | } |
630 | | |
631 | 0 | return match; |
632 | 0 | } |
633 | | |
634 | | void |
635 | | lyd_insert_after_node(struct lyd_node **first_sibling_p, struct lyd_node *sibling, struct lyd_node *node) |
636 | 0 | { |
637 | 0 | struct lyd_node *first_sibling; |
638 | |
|
639 | 0 | assert(!node->next && (node->prev == node) && (sibling != node)); |
640 | | |
641 | 0 | if (sibling->next) { |
642 | | /* sibling had a succeeding node */ |
643 | 0 | sibling->next->prev = node; |
644 | 0 | node->next = sibling->next; |
645 | 0 | } else { |
646 | | /* sibling was last, find first sibling and change its prev */ |
647 | 0 | if (first_sibling_p && *first_sibling_p) { |
648 | 0 | assert(!(*first_sibling_p)->prev->next); |
649 | 0 | (*first_sibling_p)->prev = node; |
650 | 0 | } else { |
651 | 0 | first_sibling = lyd_first_sibling(sibling); |
652 | 0 | first_sibling->prev = node; |
653 | 0 | if (first_sibling_p) { |
654 | 0 | *first_sibling_p = first_sibling; |
655 | 0 | } |
656 | 0 | } |
657 | 0 | } |
658 | 0 | node->prev = sibling; |
659 | 0 | sibling->next = node; |
660 | 0 | node->parent = sibling->parent; |
661 | |
|
662 | 0 | if (!(node->flags & LYD_DEFAULT)) { |
663 | | /* remove default flags from NP containers */ |
664 | 0 | lyd_np_cont_dflt_del(node->parent); |
665 | 0 | } |
666 | 0 | } |
667 | | |
668 | | void |
669 | | lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node) |
670 | 0 | { |
671 | 0 | assert(!node->next && (node->prev == node) && (sibling != node)); |
672 | | |
673 | 0 | node->next = sibling; |
674 | | /* covers situation of sibling being first */ |
675 | 0 | node->prev = sibling->prev; |
676 | 0 | sibling->prev = node; |
677 | 0 | if (node->prev->next) { |
678 | | /* sibling had a preceding node */ |
679 | 0 | node->prev->next = node; |
680 | 0 | } else if (sibling->parent) { |
681 | | /* sibling was first and we must also change parent child pointer */ |
682 | 0 | ((struct lyd_node_inner *)sibling->parent)->child = node; |
683 | 0 | } |
684 | 0 | node->parent = sibling->parent; |
685 | |
|
686 | 0 | if (!(node->flags & LYD_DEFAULT)) { |
687 | | /* remove default flags from NP containers */ |
688 | 0 | lyd_np_cont_dflt_del(node->parent); |
689 | 0 | } |
690 | 0 | } |
691 | | |
692 | | /** |
693 | | * @brief Insert node as the first and only child of a parent. |
694 | | * |
695 | | * Handles inserting into NP containers and key-less lists. |
696 | | * |
697 | | * @param[in] parent Parent to insert into. |
698 | | * @param[in] node Node to insert. |
699 | | */ |
700 | | static void |
701 | | lyd_insert_only_child(struct lyd_node *parent, struct lyd_node *node) |
702 | 0 | { |
703 | 0 | assert(parent && !lyd_child_any(parent) && !node->next && (node->prev == node)); |
704 | 0 | assert(!parent->schema || (parent->schema->nodetype & (LYD_NODE_INNER | LYD_NODE_ANY))); |
705 | | |
706 | 0 | ((struct lyd_node_inner *)parent)->child = node; |
707 | 0 | node->parent = parent; |
708 | |
|
709 | 0 | if (!(node->flags & LYD_DEFAULT)) { |
710 | | /* remove default flags from NP containers */ |
711 | 0 | lyd_np_cont_dflt_del(parent); |
712 | 0 | } |
713 | 0 | } |
714 | | |
715 | | /** |
716 | | * @brief Learn whether a list instance has all the keys. |
717 | | * |
718 | | * @param[in] list List instance to check. |
719 | | * @return non-zero if all the keys were found, |
720 | | * @return 0 otherwise. |
721 | | */ |
722 | | static int |
723 | | lyd_insert_has_keys(const struct lyd_node *list) |
724 | 0 | { |
725 | 0 | const struct lyd_node *key; |
726 | 0 | const struct lysc_node *skey = NULL; |
727 | |
|
728 | 0 | assert(list->schema->nodetype == LYS_LIST); |
729 | 0 | key = lyd_child(list); |
730 | 0 | while ((skey = lys_getnext(skey, list->schema, NULL, 0)) && (skey->flags & LYS_KEY)) { |
731 | 0 | if (!key || (key->schema != skey)) { |
732 | | /* key missing */ |
733 | 0 | return 0; |
734 | 0 | } |
735 | | |
736 | 0 | key = key->next; |
737 | 0 | } |
738 | | |
739 | | /* all keys found */ |
740 | 0 | return 1; |
741 | 0 | } |
742 | | |
743 | | /** |
744 | | * @brief Get the first subsequent data node that contains a different schema definition. |
745 | | * |
746 | | * @param[in] first_sibling First sibling, NULL if no top-level sibling exist yet. |
747 | | * @param[in] node Node to be inserted. |
748 | | * @return Subsequent data node with a different schema. |
749 | | */ |
750 | | static struct lyd_node * |
751 | | lyd_insert_node_find_anchor(struct lyd_node *first_sibling, struct lyd_node *node) |
752 | 0 | { |
753 | 0 | struct lyd_node *anchor; |
754 | |
|
755 | 0 | if ((node->flags & LYD_EXT) && (!first_sibling || !(first_sibling->prev->flags & LYD_EXT))) { |
756 | 0 | return NULL; |
757 | 0 | } |
758 | | |
759 | | /* find the anchor, so we can insert somewhere before it */ |
760 | 0 | anchor = lyd_insert_get_next_anchor(first_sibling, node); |
761 | | /* cannot insert data node after opaque nodes */ |
762 | 0 | if (!anchor && node->schema && first_sibling && !first_sibling->prev->schema) { |
763 | 0 | anchor = first_sibling->prev; |
764 | 0 | while ((anchor != first_sibling) && !anchor->prev->schema) { |
765 | 0 | anchor = anchor->prev; |
766 | 0 | } |
767 | 0 | } |
768 | |
|
769 | 0 | return anchor; |
770 | 0 | } |
771 | | |
772 | | /** |
773 | | * @brief Insert @p node as (usually) the last node. |
774 | | * |
775 | | * @param[in] parent Parent to insert into, NULL for top-level sibling. |
776 | | * @param[in,out] first_sibling First sibling, NULL if no top-level sibling exist yet. |
777 | | * Can be also NULL if @p parent is set. |
778 | | * @param[in] node Individual node (without siblings) to insert. |
779 | | */ |
780 | | static void |
781 | | lyd_insert_node_last(struct lyd_node *parent, struct lyd_node **first_sibling, struct lyd_node *node) |
782 | 0 | { |
783 | 0 | struct lyd_node *anchor; |
784 | |
|
785 | 0 | assert(first_sibling && node); |
786 | | |
787 | 0 | if (*first_sibling) { |
788 | | /* keep some order: data nodes, ext nodes, opaque nodes */ |
789 | 0 | anchor = (*first_sibling)->prev; |
790 | 0 | if (node->flags & LYD_EXT) { |
791 | 0 | while (!anchor->schema) { |
792 | 0 | if (anchor->prev->next) { |
793 | 0 | anchor = anchor->prev; |
794 | 0 | } else { |
795 | | /* insert as the first node */ |
796 | 0 | anchor = NULL; |
797 | 0 | break; |
798 | 0 | } |
799 | 0 | } |
800 | 0 | } |
801 | |
|
802 | 0 | if (anchor) { |
803 | 0 | lyd_insert_after_node(first_sibling, anchor, node); |
804 | 0 | } else { |
805 | 0 | lyd_insert_before_node(*first_sibling, node); |
806 | 0 | *first_sibling = node; |
807 | 0 | } |
808 | 0 | } else if (parent) { |
809 | 0 | lyd_insert_only_child(parent, node); |
810 | 0 | *first_sibling = node; |
811 | 0 | } else { |
812 | 0 | *first_sibling = node; |
813 | 0 | } |
814 | 0 | } |
815 | | |
816 | | void |
817 | | lyd_insert_node_ordby_schema(struct lyd_node *parent, struct lyd_node **first_sibling, struct lyd_node *node) |
818 | 0 | { |
819 | 0 | struct lyd_node *anchor; |
820 | |
|
821 | 0 | assert(first_sibling && node); |
822 | | |
823 | 0 | if ((anchor = lyd_insert_node_find_anchor(*first_sibling, node))) { |
824 | 0 | lyd_insert_before_node(anchor, node); |
825 | 0 | *first_sibling = *first_sibling != anchor ? *first_sibling : node; |
826 | 0 | } else if (*first_sibling && node->schema && !(*first_sibling)->prev->schema) { |
827 | | /* cannot insert data node after opaque nodes */ |
828 | 0 | anchor = (*first_sibling)->prev; |
829 | 0 | while ((anchor != *first_sibling) && !anchor->prev->schema) { |
830 | 0 | anchor = anchor->prev; |
831 | 0 | } |
832 | 0 | lyd_insert_before_node(anchor, node); |
833 | 0 | *first_sibling = *first_sibling != anchor ? *first_sibling : node; |
834 | 0 | } else { |
835 | 0 | lyd_insert_node_last(parent, first_sibling, node); |
836 | 0 | } |
837 | 0 | } |
838 | | |
839 | | void |
840 | | lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, struct lyd_node *node, uint32_t order) |
841 | 0 | { |
842 | 0 | LY_ERR ret = LY_SUCCESS; |
843 | 0 | struct lyd_node *first_sibling, *leader; |
844 | | |
845 | | /* inserting list without its keys is not supported */ |
846 | 0 | assert((parent || first_sibling_p) && node && (node->hash || !node->schema)); |
847 | 0 | assert(!parent || !parent->schema || |
848 | 0 | (parent->schema->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_RPC | LYS_ACTION | LYS_NOTIF | LYS_ANYDATA))); |
849 | | |
850 | 0 | if (!parent && first_sibling_p && (*first_sibling_p)) { |
851 | 0 | parent = (*first_sibling_p)->parent; |
852 | 0 | } |
853 | 0 | first_sibling = parent ? lyd_child_any(parent) : *first_sibling_p; |
854 | |
|
855 | 0 | if ((order == LYD_INSERT_NODE_LAST) || !node->schema || (node->flags & LYD_EXT)) { |
856 | 0 | lyd_insert_node_last(parent, &first_sibling, node); |
857 | 0 | } else if (order == LYD_INSERT_NODE_LAST_BY_SCHEMA) { |
858 | 0 | lyd_insert_node_ordby_schema(parent, &first_sibling, node); |
859 | 0 | } else if (lyds_is_supported(node) && |
860 | 0 | (lyd_find_sibling_schema(first_sibling, node->schema, &leader) == LY_SUCCESS)) { |
861 | 0 | ret = lyds_insert(&first_sibling, &leader, node); |
862 | 0 | if (ret) { |
863 | | /* The operation on the sorting tree unexpectedly failed due to some internal issue, |
864 | | * but insert the node anyway although the nodes will not be sorted. |
865 | | */ |
866 | 0 | LOGWRN(LYD_CTX(node), "Data in \"%s\" are not sorted.", node->schema->name); |
867 | 0 | lyd_insert_node_ordby_schema(parent, &first_sibling, node); |
868 | 0 | } |
869 | 0 | } else { |
870 | 0 | lyd_insert_node_ordby_schema(parent, &first_sibling, node); |
871 | 0 | } |
872 | | |
873 | | /* insert into parent HT */ |
874 | 0 | lyd_insert_hash(node); |
875 | | |
876 | | /* finish hashes for our parent, if needed and possible */ |
877 | 0 | if (node->schema && (node->schema->flags & LYS_KEY) && parent && parent->schema && lyd_insert_has_keys(parent)) { |
878 | 0 | lyd_hash(parent); |
879 | | |
880 | | /* now we can insert even the list into its parent HT */ |
881 | 0 | lyd_insert_hash(parent); |
882 | 0 | } |
883 | |
|
884 | 0 | if (first_sibling_p) { |
885 | 0 | *first_sibling_p = first_sibling; |
886 | 0 | } |
887 | |
|
888 | 0 | #ifndef NDEBUG |
889 | 0 | if ((order == LYD_INSERT_NODE_LAST) && lyds_is_supported(node) && |
890 | 0 | (node->prev->schema == node->schema) && (lyds_compare_single(node->prev, node) > 0)) { |
891 | 0 | LOGWRN(LYD_CTX(node), "Data in \"%s\" are not sorted, inserted node should not be added to the end.", |
892 | 0 | node->schema->name); |
893 | 0 | } |
894 | 0 | #endif |
895 | 0 | } |
896 | | |
897 | | /** |
898 | | * @brief Check that @p node can be unlinked. |
899 | | * |
900 | | * @param[in] node Node to check |
901 | | * @return LY_ERR value. |
902 | | */ |
903 | | static LY_ERR |
904 | | lyd_unlink_check(struct lyd_node *node) |
905 | 0 | { |
906 | 0 | if (!node) { |
907 | 0 | return LY_SUCCESS; |
908 | 0 | } |
909 | | |
910 | 0 | if (lysc_is_key(node->schema) && node->parent) { |
911 | 0 | LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.", |
912 | 0 | LYD_NAME(node)); |
913 | 0 | return LY_EINVAL; |
914 | 0 | } |
915 | | |
916 | 0 | return LY_SUCCESS; |
917 | 0 | } |
918 | | |
919 | | /** |
920 | | * @brief Move schema instances before anchor or as the last. |
921 | | * |
922 | | * The nodes will remain sorted according to the schema. |
923 | | * |
924 | | * @param[in] first_dst First sibling, destination. |
925 | | * @param[in] node Starting node, all following nodes with the same schema will be moved. |
926 | | * @param[out] next_p Next node that has a different schema or NULL. |
927 | | * @return LY_ERR value. |
928 | | */ |
929 | | static LY_ERR |
930 | | lyd_move_nodes_ordby_schema(struct lyd_node **first_dst, struct lyd_node *node, struct lyd_node **next_p) |
931 | 0 | { |
932 | 0 | struct lyd_node *second, *anchor, *iter, *next, *dst, *src, *first_src = NULL; |
933 | |
|
934 | 0 | assert(first_dst && *first_dst && !(*first_dst)->prev->next && node && next_p); |
935 | | |
936 | 0 | if ((anchor = lyd_insert_node_find_anchor(*first_dst, node))) { |
937 | | /* move the first node to the correct place according to the schema */ |
938 | 0 | LY_CHECK_RET(lyd_unlink_check(node)); |
939 | 0 | second = node->next; |
940 | 0 | lyd_unlink_ignore_lyds(&first_src, node); |
941 | 0 | lyd_insert_before_node(anchor, node); |
942 | 0 | lyd_insert_hash(node); |
943 | 0 | *first_dst = *first_dst != anchor ? *first_dst : node; |
944 | 0 | if (!second || (node->schema != second->schema)) { |
945 | | /* no more nodes to move */ |
946 | 0 | *next_p = second; |
947 | 0 | return LY_SUCCESS; |
948 | 0 | } |
949 | 0 | dst = node; |
950 | 0 | src = second; |
951 | 0 | } else { |
952 | | /* just move all instances to the end */ |
953 | 0 | dst = (*first_dst)->prev; |
954 | 0 | src = node; |
955 | 0 | } |
956 | | |
957 | | /* move the rest of source instances after @p node */ |
958 | 0 | LY_LIST_FOR_SAFE(src, next, iter) { |
959 | 0 | LY_CHECK_RET(lyd_unlink_check(iter)); |
960 | 0 | if (iter->schema != src->schema) { |
961 | 0 | break; |
962 | 0 | } |
963 | 0 | lyd_unlink_ignore_lyds(&first_src, iter); |
964 | 0 | lyd_insert_after_node(first_dst, dst, iter); |
965 | 0 | lyd_insert_hash(iter); |
966 | 0 | dst = iter; |
967 | 0 | } |
968 | 0 | *next_p = iter; |
969 | |
|
970 | 0 | return LY_SUCCESS; |
971 | 0 | } |
972 | | |
973 | | /** |
974 | | * @brief Move nodes regardless of schema. |
975 | | * |
976 | | * The destination for the move is NULL, or a childless parent. |
977 | | * |
978 | | * @param[in] parent Parent to insert into, NULL for top-level sibling. |
979 | | * @param[in] first_src First sibling, all following nodes will be moved. |
980 | | * @return LY_ERR value. |
981 | | */ |
982 | | static LY_ERR |
983 | | lyd_move_nodes_at_once(struct lyd_node *parent, struct lyd_node *first_src) |
984 | 0 | { |
985 | 0 | struct lyd_node *start, *next, *iter, *first_dst; |
986 | |
|
987 | 0 | assert(!lyd_child(parent) && first_src && !first_src->prev->next && !first_src->parent); |
988 | | |
989 | 0 | LY_CHECK_RET(lyd_unlink_check(first_src)); |
990 | | |
991 | | /* move the first node */ |
992 | 0 | start = first_src->next; |
993 | 0 | first_dst = first_src; |
994 | 0 | if (parent) { |
995 | 0 | lyd_unlink_ignore_lyds(&first_src, first_dst); |
996 | 0 | lyd_insert_only_child(parent, first_dst); |
997 | 0 | lyd_insert_hash(first_dst); |
998 | 0 | } else { |
999 | 0 | lyd_unlink_ignore_lyds(&first_src, first_dst); |
1000 | 0 | } |
1001 | | |
1002 | | /* move the rest of the nodes */ |
1003 | 0 | LY_LIST_FOR_SAFE(start, next, iter) { |
1004 | 0 | LY_CHECK_RET(lyd_unlink_check(iter)); |
1005 | 0 | lyd_unlink_ignore_lyds(&first_src, iter); |
1006 | 0 | lyd_insert_after_node(&first_dst, first_dst->prev, iter); |
1007 | 0 | lyd_insert_hash(iter); |
1008 | 0 | } |
1009 | | |
1010 | 0 | return LY_SUCCESS; |
1011 | 0 | } |
1012 | | |
1013 | | /** |
1014 | | * @brief Move the nodes in parts according to the schema. |
1015 | | * |
1016 | | * @param[in,out] first_dst First sibling, destination. |
1017 | | * @param[in] first_src First sibling, all following nodes will be moved. |
1018 | | * @return LY_ERR value. |
1019 | | */ |
1020 | | static LY_ERR |
1021 | | lyd_move_nodes_by_schema(struct lyd_node **first_dst, struct lyd_node *first_src) |
1022 | 0 | { |
1023 | 0 | LY_ERR ret; |
1024 | 0 | struct lyd_node *next, *iter, *leader; |
1025 | |
|
1026 | 0 | assert(first_dst && *first_dst && !(*first_dst)->prev->next && first_src && |
1027 | 0 | !first_src->prev->next && !first_src->parent); |
1028 | | |
1029 | 0 | for (iter = first_src; iter; iter = next) { |
1030 | 0 | if (lyds_is_supported(iter) && |
1031 | 0 | (lyd_find_sibling_schema(*first_dst, iter->schema, &leader) == LY_SUCCESS)) { |
1032 | 0 | ret = lyds_merge(first_dst, &leader, &first_src, iter, &next); |
1033 | 0 | if (ret) { |
1034 | | /* The operation on the sorting tree unexpectedly failed due to some internal issue, |
1035 | | * but insert the node anyway although the nodes will not be sorted. |
1036 | | */ |
1037 | 0 | LOGWRN(LYD_CTX(first_src), "Data in \"%s\" are not sorted.", leader->schema->name); |
1038 | 0 | LY_CHECK_RET(lyd_move_nodes_ordby_schema(first_dst, next, &next)); |
1039 | 0 | } |
1040 | 0 | } else { |
1041 | 0 | LY_CHECK_RET(lyd_move_nodes_ordby_schema(first_dst, iter, &next)); |
1042 | 0 | } |
1043 | 0 | } |
1044 | | |
1045 | 0 | return LY_SUCCESS; |
1046 | 0 | } |
1047 | | |
1048 | | /** |
1049 | | * @brief Move a nodes into parent/siblings. |
1050 | | * |
1051 | | * @param[in] parent Parent to insert into, NULL for top-level sibling. |
1052 | | * @param[in,out] first_dst_p First sibling, NULL if no top-level sibling exist yet. |
1053 | | * Can be also NULL if @p parent is set. |
1054 | | * @param[in] first_src First sibling, all following nodes will be moved. |
1055 | | * @return LY_ERR value. |
1056 | | */ |
1057 | | static LY_ERR |
1058 | | lyd_move_nodes(struct lyd_node *parent, struct lyd_node **first_dst_p, struct lyd_node *first_src) |
1059 | 0 | { |
1060 | 0 | LY_ERR ret; |
1061 | 0 | struct lyd_node *first_dst; |
1062 | |
|
1063 | 0 | assert((parent || first_dst_p) && first_src && !first_src->prev->next); |
1064 | | |
1065 | 0 | if (!first_dst_p || !*first_dst_p) { |
1066 | 0 | first_dst = lyd_child(parent); |
1067 | 0 | } else { |
1068 | 0 | first_dst = *first_dst_p; |
1069 | 0 | } |
1070 | |
|
1071 | 0 | if (first_dst) { |
1072 | 0 | ret = lyd_move_nodes_by_schema(&first_dst, first_src); |
1073 | 0 | } else { |
1074 | 0 | ret = lyd_move_nodes_at_once(parent, first_src); |
1075 | 0 | first_dst = first_src; |
1076 | 0 | } |
1077 | |
|
1078 | 0 | if (first_dst_p) { |
1079 | 0 | *first_dst_p = first_dst; |
1080 | 0 | } |
1081 | |
|
1082 | 0 | return ret; |
1083 | 0 | } |
1084 | | |
1085 | | /** |
1086 | | * @brief Check schema place of a node to be inserted. |
1087 | | * |
1088 | | * @param[in] parent Schema node of the parent data node. |
1089 | | * @param[in] sibling Schema node of a sibling data node. |
1090 | | * @param[in] schema Schema node if the data node to be inserted. |
1091 | | * @return LY_SUCCESS on success. |
1092 | | * @return LY_EINVAL if the place is invalid. |
1093 | | */ |
1094 | | static LY_ERR |
1095 | | lyd_insert_check_schema(const struct lysc_node *parent, const struct lysc_node *sibling, const struct lysc_node *schema) |
1096 | 0 | { |
1097 | 0 | const struct lysc_node *par2; |
1098 | |
|
1099 | 0 | assert(!parent || !(parent->nodetype & (LYS_CASE | LYS_CHOICE))); |
1100 | 0 | assert(!sibling || !(sibling->nodetype & (LYS_CASE | LYS_CHOICE))); |
1101 | 0 | assert(!schema || !(schema->nodetype & (LYS_CASE | LYS_CHOICE))); |
1102 | | |
1103 | 0 | if (!schema || (!parent && !sibling)) { |
1104 | | /* opaque nodes can be inserted wherever */ |
1105 | 0 | return LY_SUCCESS; |
1106 | 0 | } |
1107 | | |
1108 | 0 | if (!parent) { |
1109 | 0 | parent = lysc_data_parent(sibling); |
1110 | 0 | } |
1111 | | |
1112 | | /* find schema parent */ |
1113 | 0 | par2 = lysc_data_parent(schema); |
1114 | |
|
1115 | 0 | if (parent) { |
1116 | | /* inner node */ |
1117 | 0 | if (par2 != parent) { |
1118 | 0 | LOGERR(schema->module->ctx, LY_EINVAL, "Cannot insert, parent of \"%s\" is not \"%s\".", schema->name, |
1119 | 0 | parent->name); |
1120 | 0 | return LY_EINVAL; |
1121 | 0 | } |
1122 | 0 | } else { |
1123 | | /* top-level node */ |
1124 | 0 | if (par2) { |
1125 | 0 | LOGERR(schema->module->ctx, LY_EINVAL, "Cannot insert, node \"%s\" is not top-level.", schema->name); |
1126 | 0 | return LY_EINVAL; |
1127 | 0 | } |
1128 | 0 | } |
1129 | | |
1130 | 0 | return LY_SUCCESS; |
1131 | 0 | } |
1132 | | |
1133 | | LIBYANG_API_DEF LY_ERR |
1134 | | lyd_insert_child(struct lyd_node *parent, struct lyd_node *node) |
1135 | 0 | { |
1136 | 0 | LY_CHECK_ARG_RET(NULL, parent, node, !parent->schema || (parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL); |
1137 | |
|
1138 | 0 | if (!(node->flags & LYD_EXT)) { |
1139 | 0 | LY_CHECK_RET(lyd_insert_check_schema(parent->schema, NULL, node->schema)); |
1140 | 0 | } |
1141 | | |
1142 | 0 | if (node->parent || node->prev->next || !node->next) { |
1143 | 0 | LY_CHECK_RET(lyd_unlink_tree(node)); |
1144 | 0 | lyd_insert_node(parent, NULL, node, LYD_INSERT_NODE_DEFAULT); |
1145 | 0 | } else { |
1146 | 0 | LY_CHECK_RET(lyd_move_nodes(parent, NULL, node)); |
1147 | 0 | } |
1148 | | |
1149 | 0 | return LY_SUCCESS; |
1150 | 0 | } |
1151 | | |
1152 | | LIBYANG_API_DEF LY_ERR |
1153 | | lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_node **first) |
1154 | 0 | { |
1155 | 0 | struct lyd_node *first_sibling; |
1156 | |
|
1157 | 0 | LY_CHECK_ARG_RET(NULL, node, sibling != node, LY_EINVAL); |
1158 | |
|
1159 | 0 | if (sibling) { |
1160 | 0 | LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema)); |
1161 | 0 | } |
1162 | | |
1163 | 0 | first_sibling = lyd_first_sibling(sibling); |
1164 | 0 | if (node->parent || node->prev->next || !node->next) { |
1165 | 0 | LY_CHECK_RET(lyd_unlink_tree(node)); |
1166 | 0 | lyd_insert_node(NULL, &first_sibling, node, LYD_INSERT_NODE_DEFAULT); |
1167 | 0 | } else { |
1168 | 0 | LY_CHECK_RET(lyd_move_nodes(NULL, &first_sibling, node)); |
1169 | 0 | } |
1170 | | |
1171 | 0 | if (first) { |
1172 | 0 | *first = first_sibling; |
1173 | 0 | } |
1174 | |
|
1175 | 0 | return LY_SUCCESS; |
1176 | 0 | } |
1177 | | |
1178 | | LIBYANG_API_DEF LY_ERR |
1179 | | lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node) |
1180 | 0 | { |
1181 | 0 | LY_CHECK_ARG_RET(NULL, sibling, node, sibling != node, LY_EINVAL); |
1182 | 0 | LY_CHECK_CTX_EQUAL_RET(__func__, LYD_CTX(sibling), LYD_CTX(node), LY_EINVAL); |
1183 | |
|
1184 | 0 | LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema)); |
1185 | |
|
1186 | 0 | if (node->schema && (!(node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) || !(node->schema->flags & LYS_ORDBY_USER))) { |
1187 | 0 | LOGERR(LYD_CTX(sibling), LY_EINVAL, "Can be used only for user-ordered nodes."); |
1188 | 0 | return LY_EINVAL; |
1189 | 0 | } |
1190 | 0 | if (node->schema && sibling->schema && (node->schema != sibling->schema)) { |
1191 | 0 | LOGERR(LYD_CTX(sibling), LY_EINVAL, "Cannot insert before a different schema node instance."); |
1192 | 0 | return LY_EINVAL; |
1193 | 0 | } |
1194 | | |
1195 | 0 | lyd_unlink(node); |
1196 | 0 | lyd_insert_before_node(sibling, node); |
1197 | 0 | lyd_insert_hash(node); |
1198 | |
|
1199 | 0 | return LY_SUCCESS; |
1200 | 0 | } |
1201 | | |
1202 | | LIBYANG_API_DEF LY_ERR |
1203 | | lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node) |
1204 | 0 | { |
1205 | 0 | LY_CHECK_ARG_RET(NULL, sibling, node, sibling != node, LY_EINVAL); |
1206 | 0 | LY_CHECK_CTX_EQUAL_RET(__func__, LYD_CTX(sibling), LYD_CTX(node), LY_EINVAL); |
1207 | |
|
1208 | 0 | LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema)); |
1209 | |
|
1210 | 0 | if (node->schema && (!(node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) || !(node->schema->flags & LYS_ORDBY_USER))) { |
1211 | 0 | LOGERR(LYD_CTX(sibling), LY_EINVAL, "Can be used only for user-ordered nodes."); |
1212 | 0 | return LY_EINVAL; |
1213 | 0 | } |
1214 | 0 | if (node->schema && sibling->schema && (node->schema != sibling->schema)) { |
1215 | 0 | LOGERR(LYD_CTX(sibling), LY_EINVAL, "Cannot insert after a different schema node instance."); |
1216 | 0 | return LY_EINVAL; |
1217 | 0 | } |
1218 | | |
1219 | 0 | lyd_unlink(node); |
1220 | 0 | lyd_insert_after_node(NULL, sibling, node); |
1221 | 0 | lyd_insert_hash(node); |
1222 | |
|
1223 | 0 | return LY_SUCCESS; |
1224 | 0 | } |
1225 | | |
1226 | | void |
1227 | | lyd_unlink_ignore_lyds(struct lyd_node **first_sibling_p, struct lyd_node *node) |
1228 | 0 | { |
1229 | 0 | struct lyd_node *first_sibling; |
1230 | | |
1231 | | /* update hashes while still linked into the tree */ |
1232 | 0 | lyd_unlink_hash(node); |
1233 | | |
1234 | | /* unlink leafref nodes */ |
1235 | 0 | if (node->schema && (node->schema->nodetype & LYD_NODE_TERM)) { |
1236 | 0 | lyd_free_leafref_nodes((struct lyd_node_term *)node); |
1237 | 0 | } |
1238 | | |
1239 | | /* unlink from siblings */ |
1240 | 0 | if (node->next) { |
1241 | 0 | node->next->prev = node->prev; |
1242 | 0 | if (node->prev->next) { |
1243 | 0 | node->prev->next = node->next; |
1244 | 0 | } else if (first_sibling_p) { |
1245 | | /* unlinking the first node */ |
1246 | 0 | *first_sibling_p = node->next; |
1247 | 0 | } |
1248 | 0 | } else { |
1249 | | /* unlinking the last node */ |
1250 | | /* update the "last" pointer from the first node */ |
1251 | 0 | if (first_sibling_p && *first_sibling_p) { |
1252 | 0 | (*first_sibling_p)->prev = node->prev; |
1253 | 0 | } else { |
1254 | 0 | first_sibling = lyd_first_sibling(node); |
1255 | 0 | first_sibling->prev = node->prev; |
1256 | 0 | if (first_sibling_p) { |
1257 | 0 | *first_sibling_p = first_sibling; |
1258 | 0 | } |
1259 | 0 | } |
1260 | 0 | node->prev->next = NULL; |
1261 | 0 | } |
1262 | | |
1263 | | /* unlink from parent */ |
1264 | 0 | if (node->parent) { |
1265 | 0 | if (((struct lyd_node_inner *)node->parent)->child == node) { |
1266 | | /* the node is the first child */ |
1267 | 0 | ((struct lyd_node_inner *)node->parent)->child = node->next; |
1268 | 0 | } |
1269 | | |
1270 | | /* check for NP container whether its last non-default node is not being unlinked */ |
1271 | 0 | lyd_np_cont_dflt_set(node->parent); |
1272 | |
|
1273 | 0 | node->parent = NULL; |
1274 | 0 | } |
1275 | |
|
1276 | 0 | node->next = NULL; |
1277 | 0 | node->prev = node; |
1278 | 0 | } |
1279 | | |
1280 | | void |
1281 | | lyd_unlink(struct lyd_node *node) |
1282 | 0 | { |
1283 | 0 | struct lyd_node *leader; |
1284 | |
|
1285 | 0 | if (!node) { |
1286 | 0 | return; |
1287 | 0 | } |
1288 | | |
1289 | | /* unlink from the lyds tree */ |
1290 | 0 | if (lyds_is_supported(node)) { |
1291 | 0 | if (!node->prev->next || (node->prev->schema != node->schema)) { |
1292 | 0 | leader = node; |
1293 | 0 | } else { |
1294 | 0 | lyd_find_sibling_val(node, node->schema, NULL, 0, &leader); |
1295 | 0 | assert(leader); |
1296 | 0 | } |
1297 | 0 | lyds_unlink(&leader, node); |
1298 | 0 | } |
1299 | | |
1300 | | /* unlink data tree */ |
1301 | 0 | lyd_unlink_ignore_lyds(NULL, node); |
1302 | 0 | } |
1303 | | |
1304 | | LIBYANG_API_DEF LY_ERR |
1305 | | lyd_unlink_siblings(struct lyd_node *node) |
1306 | 0 | { |
1307 | 0 | struct lyd_node *next, *iter, *leader, *start, *first_sibling = NULL; |
1308 | |
|
1309 | 0 | if (lyds_is_supported(node) && node->prev->next && (node->prev->schema == node->schema)) { |
1310 | | /* unlink starts at the non-first item in the (leaf-)list */ |
1311 | 0 | lyd_find_sibling_val(node, node->schema, NULL, 0, &leader); |
1312 | 0 | lyds_split(&first_sibling, leader, node, &start); |
1313 | 0 | } else { |
1314 | | /* unlink @p node */ |
1315 | 0 | LY_CHECK_RET(lyd_unlink_check(node)); |
1316 | 0 | start = node->next; |
1317 | 0 | lyd_unlink_ignore_lyds(&first_sibling, node); |
1318 | 0 | } |
1319 | | |
1320 | | /* continue unlinking the rest */ |
1321 | 0 | LY_LIST_FOR_SAFE(start, next, iter) { |
1322 | 0 | LY_CHECK_RET(lyd_unlink_check(iter)); |
1323 | 0 | lyd_unlink_ignore_lyds(&first_sibling, iter); |
1324 | 0 | lyd_insert_after_node(&node, node->prev, iter); |
1325 | 0 | lyd_insert_hash(iter); |
1326 | 0 | } |
1327 | | |
1328 | 0 | return LY_SUCCESS; |
1329 | 0 | } |
1330 | | |
1331 | | LIBYANG_API_DEF LY_ERR |
1332 | | lyd_unlink_tree(struct lyd_node *node) |
1333 | 0 | { |
1334 | 0 | LY_CHECK_RET(lyd_unlink_check(node)); |
1335 | 0 | lyd_unlink(node); |
1336 | |
|
1337 | 0 | return LY_SUCCESS; |
1338 | 0 | } |
1339 | | |
1340 | | void |
1341 | | lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_dflt) |
1342 | 0 | { |
1343 | 0 | struct lyd_meta *last, *iter; |
1344 | |
|
1345 | 0 | assert(parent); |
1346 | | |
1347 | 0 | if (!meta) { |
1348 | 0 | return; |
1349 | 0 | } |
1350 | | |
1351 | 0 | for (iter = meta; iter; iter = iter->next) { |
1352 | 0 | iter->parent = parent; |
1353 | 0 | } |
1354 | | |
1355 | | /* insert as the last attribute */ |
1356 | 0 | if (parent->meta) { |
1357 | 0 | for (last = parent->meta; last->next; last = last->next) {} |
1358 | 0 | last->next = meta; |
1359 | 0 | } else { |
1360 | 0 | parent->meta = meta; |
1361 | 0 | } |
1362 | | |
1363 | | /* remove default flags from NP containers */ |
1364 | 0 | if (clear_dflt) { |
1365 | 0 | lyd_np_cont_dflt_del(parent); |
1366 | 0 | } |
1367 | 0 | } |
1368 | | |
1369 | | void |
1370 | | lyd_unlink_meta_single(struct lyd_meta *meta) |
1371 | 0 | { |
1372 | 0 | struct lyd_meta *iter; |
1373 | |
|
1374 | 0 | if (!meta) { |
1375 | 0 | return; |
1376 | 0 | } |
1377 | | |
1378 | 0 | if (meta->parent && (meta->parent->meta == meta)) { |
1379 | 0 | meta->parent->meta = meta->next; |
1380 | 0 | } else if (meta->parent) { |
1381 | 0 | for (iter = meta->parent->meta; iter->next && (iter->next != meta); iter = iter->next) {} |
1382 | 0 | if (iter->next) { |
1383 | 0 | iter->next = meta->next; |
1384 | 0 | } |
1385 | 0 | } |
1386 | |
|
1387 | 0 | meta->next = NULL; |
1388 | 0 | meta->parent = NULL; |
1389 | 0 | } |
1390 | | |
1391 | | struct lysc_ext_instance * |
1392 | | lyd_get_meta_annotation(const struct lys_module *mod, const char *name, size_t name_len) |
1393 | 0 | { |
1394 | 0 | LY_ARRAY_COUNT_TYPE u; |
1395 | 0 | struct lyplg_ext *plugin; |
1396 | |
|
1397 | 0 | if (!mod) { |
1398 | 0 | return NULL; |
1399 | 0 | } |
1400 | | |
1401 | 0 | LY_ARRAY_FOR(mod->compiled->exts, u) { |
1402 | 0 | plugin = LYSC_GET_EXT_PLG(mod->compiled->exts[u].def->plugin_ref); |
1403 | 0 | if (plugin && !strcmp(plugin->id, "ly2 metadata") && |
1404 | 0 | !ly_strncmp(mod->compiled->exts[u].argument, name, name_len)) { |
1405 | 0 | return &mod->compiled->exts[u]; |
1406 | 0 | } |
1407 | 0 | } |
1408 | | |
1409 | 0 | return NULL; |
1410 | 0 | } |
1411 | | |
1412 | | LY_ERR |
1413 | | lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name, |
1414 | | uint32_t name_len, const void *value, uint64_t value_size_bits, ly_bool is_utf8, ly_bool store_only, |
1415 | | ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, |
1416 | | const struct lyd_node *lnode, ly_bool clear_dflt, ly_bool *incomplete) |
1417 | 0 | { |
1418 | 0 | LY_ERR ret = LY_SUCCESS; |
1419 | 0 | struct lysc_ext_instance *ant = NULL; |
1420 | 0 | const struct lysc_type *ant_type; |
1421 | 0 | struct lyd_meta *mt, *last; |
1422 | |
|
1423 | 0 | assert((parent || meta) && mod); |
1424 | | |
1425 | 0 | ant = lyd_get_meta_annotation(mod, name, name_len); |
1426 | 0 | if (!ant) { |
1427 | | /* attribute is not defined as a metadata annotation (RFC 7952) */ |
1428 | 0 | if (!parent && ctx_node) { |
1429 | 0 | LOG_LOCSET(ctx_node); |
1430 | 0 | } |
1431 | 0 | LOGVAL(mod->ctx, parent, LYVE_REFERENCE, "Annotation definition for attribute \"%s:%.*s\" not found.", |
1432 | 0 | mod->name, (int)name_len, name); |
1433 | 0 | if (!parent && ctx_node) { |
1434 | 0 | LOG_LOCBACK(1); |
1435 | 0 | } |
1436 | 0 | ret = LY_EINVAL; |
1437 | 0 | goto cleanup; |
1438 | 0 | } |
1439 | | |
1440 | 0 | mt = calloc(1, sizeof *mt); |
1441 | 0 | LY_CHECK_ERR_GOTO(!mt, LOGMEM(mod->ctx); ret = LY_EMEM, cleanup); |
1442 | 0 | mt->parent = parent; |
1443 | 0 | mt->annotation = ant; |
1444 | 0 | lyplg_ext_get_storage(ant, LY_STMT_TYPE, sizeof ant_type, (const void **)&ant_type); |
1445 | 0 | ret = lyd_value_store(mod->ctx, lnode, &mt->value, ant_type, value, value_size_bits, is_utf8, store_only, dynamic, |
1446 | 0 | format, prefix_data, hints, ctx_node, incomplete); |
1447 | 0 | LY_CHECK_ERR_GOTO(ret, free(mt), cleanup); |
1448 | 0 | ret = lydict_insert(mod->ctx, name, name_len, &mt->name); |
1449 | 0 | LY_CHECK_ERR_GOTO(ret, free(mt), cleanup); |
1450 | | |
1451 | | /* insert as the last attribute */ |
1452 | 0 | if (parent) { |
1453 | 0 | lyd_insert_meta(parent, mt, clear_dflt); |
1454 | 0 | } else if (*meta) { |
1455 | 0 | for (last = *meta; last->next; last = last->next) {} |
1456 | 0 | last->next = mt; |
1457 | 0 | } |
1458 | |
|
1459 | 0 | if (meta) { |
1460 | 0 | *meta = mt; |
1461 | 0 | } |
1462 | |
|
1463 | 0 | cleanup: |
1464 | 0 | return ret; |
1465 | 0 | } |
1466 | | |
1467 | | void |
1468 | | lyd_insert_attr(struct lyd_node *parent, struct lyd_attr *attr) |
1469 | 0 | { |
1470 | 0 | struct lyd_attr *last, *iter; |
1471 | 0 | struct lyd_node_opaq *opaq; |
1472 | |
|
1473 | 0 | assert(parent && !parent->schema); |
1474 | | |
1475 | 0 | if (!attr) { |
1476 | 0 | return; |
1477 | 0 | } |
1478 | | |
1479 | 0 | opaq = (struct lyd_node_opaq *)parent; |
1480 | 0 | for (iter = attr; iter; iter = iter->next) { |
1481 | 0 | iter->parent = opaq; |
1482 | 0 | } |
1483 | | |
1484 | | /* insert as the last attribute */ |
1485 | 0 | if (opaq->attr) { |
1486 | 0 | for (last = opaq->attr; last->next; last = last->next) {} |
1487 | 0 | last->next = attr; |
1488 | 0 | } else { |
1489 | 0 | opaq->attr = attr; |
1490 | 0 | } |
1491 | 0 | } |
1492 | | |
1493 | | LY_ERR |
1494 | | lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct ly_ctx *ctx, const char *name, uint32_t name_len, |
1495 | | const char *prefix, uint32_t prefix_len, const char *module_key, uint32_t module_key_len, const char *value, |
1496 | | uint32_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints) |
1497 | 0 | { |
1498 | 0 | LY_ERR ret = LY_SUCCESS; |
1499 | 0 | struct lyd_attr *at, *last; |
1500 | |
|
1501 | 0 | assert(ctx && (parent || attr) && (!parent || !parent->schema)); |
1502 | 0 | assert(name && name_len && format); |
1503 | | |
1504 | 0 | if (!value_len && (!dynamic || !*dynamic)) { |
1505 | 0 | value = ""; |
1506 | 0 | } |
1507 | |
|
1508 | 0 | at = calloc(1, sizeof *at); |
1509 | 0 | LY_CHECK_ERR_RET(!at, LOGMEM(ctx); ly_free_prefix_data(format, val_prefix_data), LY_EMEM); |
1510 | |
|
1511 | 0 | LY_CHECK_GOTO(ret = lydict_insert(ctx, name, name_len, &at->name.name), finish); |
1512 | 0 | if (prefix_len) { |
1513 | 0 | LY_CHECK_GOTO(ret = lydict_insert(ctx, prefix, prefix_len, &at->name.prefix), finish); |
1514 | 0 | } |
1515 | 0 | if (module_key_len) { |
1516 | 0 | LY_CHECK_GOTO(ret = lydict_insert(ctx, module_key, module_key_len, &at->name.module_ns), finish); |
1517 | 0 | } |
1518 | | |
1519 | 0 | if (dynamic && *dynamic) { |
1520 | 0 | ret = lydict_insert_zc(ctx, (char *)value, &at->value); |
1521 | 0 | LY_CHECK_GOTO(ret, finish); |
1522 | 0 | *dynamic = 0; |
1523 | 0 | } else { |
1524 | 0 | LY_CHECK_GOTO(ret = lydict_insert(ctx, value, value_len, &at->value), finish); |
1525 | 0 | } |
1526 | 0 | at->format = format; |
1527 | 0 | at->val_prefix_data = val_prefix_data; |
1528 | 0 | at->hints = hints; |
1529 | | |
1530 | | /* insert as the last attribute */ |
1531 | 0 | if (parent) { |
1532 | 0 | lyd_insert_attr(parent, at); |
1533 | 0 | } else if (*attr) { |
1534 | 0 | for (last = *attr; last->next; last = last->next) {} |
1535 | 0 | last->next = at; |
1536 | 0 | } |
1537 | |
|
1538 | 0 | finish: |
1539 | 0 | if (ret) { |
1540 | 0 | lyd_free_attr_single(ctx, at); |
1541 | 0 | } else if (attr) { |
1542 | 0 | *attr = at; |
1543 | 0 | } |
1544 | 0 | return LY_SUCCESS; |
1545 | 0 | } |
1546 | | |
1547 | | ly_bool |
1548 | | lyd_compare_schema_equal(const struct lysc_node *schema1, const struct lysc_node *schema2, ly_bool cmp_parents) |
1549 | 0 | { |
1550 | 0 | if (!schema1 && !schema2) { |
1551 | 0 | return 1; |
1552 | 0 | } else if (!schema1 || !schema2) { |
1553 | 0 | return 0; |
1554 | 0 | } |
1555 | | |
1556 | 0 | if (schema1->module->ctx == schema2->module->ctx) { |
1557 | 0 | return (schema1 == schema2) ? 1 : 0; |
1558 | 0 | } |
1559 | | |
1560 | 0 | do { |
1561 | 0 | if (schema1->nodetype != schema2->nodetype) { |
1562 | 0 | return 0; |
1563 | 0 | } |
1564 | | |
1565 | 0 | if (strcmp(schema1->name, schema2->name)) { |
1566 | 0 | return 0; |
1567 | 0 | } |
1568 | | |
1569 | 0 | if (strcmp(schema1->module->name, schema2->module->name)) { |
1570 | 0 | return 0; |
1571 | 0 | } |
1572 | | |
1573 | 0 | schema1 = schema1->parent; |
1574 | 0 | schema2 = schema2->parent; |
1575 | 0 | } while (cmp_parents && schema1 && schema2); |
1576 | | |
1577 | 0 | if ((schema1 && !schema2) || (!schema1 && schema2)) { |
1578 | 0 | return 0; |
1579 | 0 | } |
1580 | | |
1581 | 0 | return 1; |
1582 | 0 | } |
1583 | | |
1584 | | /** |
1585 | | * @brief Check the equality of the schemas for all parent nodes. |
1586 | | * |
1587 | | * Both nodes must be from different contexts. |
1588 | | * |
1589 | | * @param node1 Data of first node. |
1590 | | * @param node2 Data of second node. |
1591 | | * @return 1 if the all related parental schemas are equal otherwise 0. |
1592 | | */ |
1593 | | static ly_bool |
1594 | | lyd_compare_schema_parents_equal(const struct lyd_node *node1, const struct lyd_node *node2) |
1595 | 0 | { |
1596 | 0 | const struct lysc_node *parent1, *parent2; |
1597 | |
|
1598 | 0 | assert(node1 && node2); |
1599 | | |
1600 | 0 | for (parent1 = node1->schema->parent, parent2 = node2->schema->parent; |
1601 | 0 | parent1 && parent2; |
1602 | 0 | parent1 = parent1->parent, parent2 = parent2->parent) { |
1603 | 0 | if (!lyd_compare_schema_equal(parent1, parent2, 0)) { |
1604 | 0 | return 0; |
1605 | 0 | } |
1606 | 0 | } |
1607 | | |
1608 | 0 | if (parent1 || parent2) { |
1609 | 0 | return 0; |
1610 | 0 | } |
1611 | | |
1612 | 0 | return 1; |
1613 | 0 | } |
1614 | | |
1615 | | /** |
1616 | | * @brief Compare 2 nodes values including opaque node values. |
1617 | | * |
1618 | | * @param[in] node1 First node to compare. |
1619 | | * @param[in] node2 Second node to compare. |
1620 | | * @return LY_SUCCESS if equal. |
1621 | | * @return LY_ENOT if not equal. |
1622 | | * @return LY_ERR on error. |
1623 | | */ |
1624 | | static LY_ERR |
1625 | | lyd_compare_single_value(const struct lyd_node *node1, const struct lyd_node *node2) |
1626 | 0 | { |
1627 | 0 | const struct lyd_node_opaq *opaq1 = NULL, *opaq2 = NULL; |
1628 | 0 | const char *val1, *val2, *col; |
1629 | 0 | const struct lys_module *mod; |
1630 | 0 | char *val_dyn = NULL; |
1631 | 0 | LY_ERR rc = LY_SUCCESS; |
1632 | |
|
1633 | 0 | if (!node1->schema) { |
1634 | 0 | opaq1 = (struct lyd_node_opaq *)node1; |
1635 | 0 | } |
1636 | 0 | if (!node2->schema) { |
1637 | 0 | opaq2 = (struct lyd_node_opaq *)node2; |
1638 | 0 | } |
1639 | |
|
1640 | 0 | if (opaq1 && opaq2 && (opaq1->format == LY_VALUE_XML) && (opaq2->format == LY_VALUE_XML)) { |
1641 | | /* opaque XML and opaque XML node */ |
1642 | 0 | if (lyxml_value_compare(LYD_CTX(node1), opaq1->value, opaq1->val_prefix_data, LYD_CTX(node2), opaq2->value, |
1643 | 0 | opaq2->val_prefix_data)) { |
1644 | 0 | return LY_ENOT; |
1645 | 0 | } |
1646 | 0 | return LY_SUCCESS; |
1647 | 0 | } |
1648 | | |
1649 | | /* get their values */ |
1650 | 0 | if (opaq1 && ((opaq1->format == LY_VALUE_XML) || (opaq1->format == LY_VALUE_STR_NS)) && (col = strchr(opaq1->value, ':'))) { |
1651 | | /* XML value with a prefix, try to transform it into a JSON (canonical) value */ |
1652 | 0 | mod = ly_resolve_prefix(LYD_CTX(node1), opaq1->value, col - opaq1->value, opaq1->format, opaq1->val_prefix_data); |
1653 | 0 | if (!mod) { |
1654 | | /* unable to compare */ |
1655 | 0 | return LY_ENOT; |
1656 | 0 | } |
1657 | | |
1658 | 0 | if (asprintf(&val_dyn, "%s%s", mod->name, col) == -1) { |
1659 | 0 | LOGMEM(LYD_CTX(node1)); |
1660 | 0 | return LY_EMEM; |
1661 | 0 | } |
1662 | 0 | val1 = val_dyn; |
1663 | 0 | } else { |
1664 | 0 | val1 = lyd_get_value(node1); |
1665 | 0 | } |
1666 | 0 | if (opaq2 && ((opaq2->format == LY_VALUE_XML) || (opaq2->format == LY_VALUE_STR_NS)) && (col = strchr(opaq2->value, ':'))) { |
1667 | 0 | mod = ly_resolve_prefix(LYD_CTX(node2), opaq2->value, col - opaq2->value, opaq2->format, opaq2->val_prefix_data); |
1668 | 0 | if (!mod) { |
1669 | 0 | return LY_ENOT; |
1670 | 0 | } |
1671 | | |
1672 | 0 | assert(!val_dyn); |
1673 | 0 | if (asprintf(&val_dyn, "%s%s", mod->name, col) == -1) { |
1674 | 0 | LOGMEM(LYD_CTX(node2)); |
1675 | 0 | return LY_EMEM; |
1676 | 0 | } |
1677 | 0 | val2 = val_dyn; |
1678 | 0 | } else { |
1679 | 0 | val2 = lyd_get_value(node2); |
1680 | 0 | } |
1681 | | |
1682 | | /* compare values */ |
1683 | 0 | if (strcmp(val1, val2)) { |
1684 | 0 | rc = LY_ENOT; |
1685 | 0 | } |
1686 | |
|
1687 | 0 | free(val_dyn); |
1688 | 0 | return rc; |
1689 | 0 | } |
1690 | | |
1691 | | /** |
1692 | | * @brief Compare 2 data nodes if they are equivalent regarding the schema tree. |
1693 | | * |
1694 | | * Works correctly even if @p node1 and @p node2 have different contexts. |
1695 | | * |
1696 | | * @param[in] node1 The first node to compare. |
1697 | | * @param[in] node2 The second node to compare. |
1698 | | * @param[in] options Various @ref datacompareoptions. |
1699 | | * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match. |
1700 | | * @return LY_SUCCESS if the nodes are equivalent. |
1701 | | * @return LY_ENOT if the nodes are not equivalent. |
1702 | | */ |
1703 | | static LY_ERR |
1704 | | lyd_compare_single_schema(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options, |
1705 | | ly_bool parental_schemas_checked) |
1706 | 0 | { |
1707 | 0 | if (LYD_CTX(node1) == LYD_CTX(node2)) { |
1708 | | /* same contexts */ |
1709 | 0 | if (options & LYD_COMPARE_OPAQ) { |
1710 | 0 | if (lyd_node_schema(node1) != lyd_node_schema(node2)) { |
1711 | 0 | return LY_ENOT; |
1712 | 0 | } |
1713 | 0 | } else { |
1714 | 0 | if (node1->schema != node2->schema) { |
1715 | 0 | return LY_ENOT; |
1716 | 0 | } |
1717 | 0 | } |
1718 | 0 | } else { |
1719 | | /* different contexts */ |
1720 | 0 | if (!lyd_compare_schema_equal(node1->schema, node2->schema, 0)) { |
1721 | 0 | return LY_ENOT; |
1722 | 0 | } |
1723 | 0 | if (!parental_schemas_checked) { |
1724 | 0 | if (!lyd_compare_schema_parents_equal(node1, node2)) { |
1725 | 0 | return LY_ENOT; |
1726 | 0 | } |
1727 | 0 | parental_schemas_checked = 1; |
1728 | 0 | } |
1729 | 0 | } |
1730 | | |
1731 | 0 | return LY_SUCCESS; |
1732 | 0 | } |
1733 | | |
1734 | | /** |
1735 | | * @brief Compare 2 data nodes if they are equivalent regarding the data they contain. |
1736 | | * |
1737 | | * Works correctly even if @p node1 and @p node2 have different contexts. |
1738 | | * |
1739 | | * @param[in] node1 The first node to compare. |
1740 | | * @param[in] node2 The second node to compare. |
1741 | | * @param[in] options Various @ref datacompareoptions. |
1742 | | * @return LY_SUCCESS if the nodes are equivalent. |
1743 | | * @return LY_ENOT if the nodes are not equivalent. |
1744 | | */ |
1745 | | static LY_ERR |
1746 | | lyd_compare_single_data(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) |
1747 | 0 | { |
1748 | 0 | const struct lyd_node *iter1, *iter2; |
1749 | 0 | struct lyd_node_any *any1, *any2; |
1750 | 0 | int len1, len2; |
1751 | 0 | LY_ERR r; |
1752 | |
|
1753 | 0 | if (!(options & LYD_COMPARE_OPAQ) && (node1->hash != node2->hash)) { |
1754 | 0 | return LY_ENOT; |
1755 | 0 | } |
1756 | | /* equal hashes do not mean equal nodes, they can be just in collision so the nodes must be checked explicitly */ |
1757 | | |
1758 | 0 | if (!node1->schema || !node2->schema) { |
1759 | 0 | if (!(options & LYD_COMPARE_OPAQ) && ((node1->schema && !node2->schema) || (!node1->schema && node2->schema))) { |
1760 | 0 | return LY_ENOT; |
1761 | 0 | } |
1762 | 0 | if ((!node1->schema && !node2->schema) || (node1->schema && (node1->schema->nodetype & LYD_NODE_TERM)) || |
1763 | 0 | (node2->schema && (node2->schema->nodetype & LYD_NODE_TERM))) { |
1764 | | /* compare values only if there are any to compare */ |
1765 | 0 | if ((r = lyd_compare_single_value(node1, node2))) { |
1766 | 0 | return r; |
1767 | 0 | } |
1768 | 0 | } |
1769 | | |
1770 | 0 | if (options & LYD_COMPARE_FULL_RECURSION) { |
1771 | 0 | return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1); |
1772 | 0 | } |
1773 | 0 | return LY_SUCCESS; |
1774 | 0 | } else { |
1775 | 0 | switch (node1->schema->nodetype) { |
1776 | 0 | case LYS_LEAF: |
1777 | 0 | case LYS_LEAFLIST: |
1778 | 0 | if (options & LYD_COMPARE_DEFAULTS) { |
1779 | 0 | if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) { |
1780 | 0 | return LY_ENOT; |
1781 | 0 | } |
1782 | 0 | } |
1783 | 0 | if ((r = lyd_compare_single_value(node1, node2))) { |
1784 | 0 | return r; |
1785 | 0 | } |
1786 | | |
1787 | 0 | return LY_SUCCESS; |
1788 | 0 | case LYS_CONTAINER: |
1789 | 0 | case LYS_RPC: |
1790 | 0 | case LYS_ACTION: |
1791 | 0 | case LYS_NOTIF: |
1792 | | /* implicit container is always equal to a container with non-default descendants */ |
1793 | 0 | if (options & LYD_COMPARE_FULL_RECURSION) { |
1794 | 0 | return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1); |
1795 | 0 | } |
1796 | 0 | return LY_SUCCESS; |
1797 | 0 | case LYS_LIST: |
1798 | 0 | iter1 = lyd_child(node1); |
1799 | 0 | iter2 = lyd_child(node2); |
1800 | |
|
1801 | 0 | if (options & LYD_COMPARE_FULL_RECURSION) { |
1802 | 0 | return lyd_compare_siblings_(iter1, iter2, options, 1); |
1803 | 0 | } else if (node1->schema->flags & LYS_KEYLESS) { |
1804 | | /* always equal */ |
1805 | 0 | return LY_SUCCESS; |
1806 | 0 | } |
1807 | | |
1808 | | /* lists with keys, their equivalence is based on their keys */ |
1809 | 0 | for (const struct lysc_node *key = lysc_node_child(node1->schema); |
1810 | 0 | key && (key->flags & LYS_KEY); |
1811 | 0 | key = key->next) { |
1812 | 0 | if (!iter1 || !iter2) { |
1813 | 0 | return (iter1 == iter2) ? LY_SUCCESS : LY_ENOT; |
1814 | 0 | } |
1815 | 0 | r = lyd_compare_single_schema(iter1, iter2, options, 1); |
1816 | 0 | LY_CHECK_RET(r); |
1817 | 0 | r = lyd_compare_single_data(iter1, iter2, options); |
1818 | 0 | LY_CHECK_RET(r); |
1819 | |
|
1820 | 0 | iter1 = iter1->next; |
1821 | 0 | iter2 = iter2->next; |
1822 | 0 | } |
1823 | | |
1824 | 0 | return LY_SUCCESS; |
1825 | 0 | case LYS_ANYXML: |
1826 | 0 | case LYS_ANYDATA: |
1827 | 0 | any1 = (struct lyd_node_any *)node1; |
1828 | 0 | any2 = (struct lyd_node_any *)node2; |
1829 | |
|
1830 | 0 | if (!any1->child && !any1->value && !any2->child && !any2->value) { |
1831 | | /* empty */ |
1832 | 0 | return LY_SUCCESS; |
1833 | 0 | } |
1834 | 0 | if ((any1->child && !any2->child) || (any1->value && !any2->value)) { |
1835 | | /* different value type */ |
1836 | 0 | return LY_ENOT; |
1837 | 0 | } |
1838 | 0 | if (any1->child) { |
1839 | 0 | return lyd_compare_siblings_(any1->child, any2->child, options, 1); |
1840 | 0 | } else { |
1841 | 0 | assert(any1->value && any2->value); |
1842 | | |
1843 | 0 | len1 = strlen(any1->value); |
1844 | 0 | len2 = strlen(any2->value); |
1845 | 0 | if ((len1 != len2) || strcmp(any1->value, any2->value)) { |
1846 | 0 | return LY_ENOT; |
1847 | 0 | } |
1848 | 0 | return LY_SUCCESS; |
1849 | 0 | } |
1850 | 0 | } |
1851 | 0 | } |
1852 | | |
1853 | 0 | LOGINT(LYD_CTX(node1)); |
1854 | 0 | return LY_EINT; |
1855 | 0 | } |
1856 | | |
1857 | | /** |
1858 | | * @brief Compare all siblings at a node level. |
1859 | | * |
1860 | | * @param[in] node1 First sibling list. |
1861 | | * @param[in] node2 Second sibling list. |
1862 | | * @param[in] options Various @ref datacompareoptions. |
1863 | | * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match. |
1864 | | * @return LY_SUCCESS if equal. |
1865 | | * @return LY_ENOT if not equal. |
1866 | | * @return LY_ERR on error. |
1867 | | */ |
1868 | | static LY_ERR |
1869 | | lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options, |
1870 | | ly_bool parental_schemas_checked) |
1871 | 0 | { |
1872 | 0 | LY_ERR r; |
1873 | 0 | const struct lyd_node *iter2; |
1874 | |
|
1875 | 0 | while (node1 && node2) { |
1876 | | /* schema match */ |
1877 | 0 | r = lyd_compare_single_schema(node1, node2, options, parental_schemas_checked); |
1878 | 0 | LY_CHECK_RET(r); |
1879 | |
|
1880 | 0 | if (node1->schema && (((node1->schema->nodetype == LYS_LIST) && !(node1->schema->flags & LYS_KEYLESS)) || |
1881 | 0 | ((node1->schema->nodetype == LYS_LEAFLIST) && (node1->schema->flags & LYS_CONFIG_W))) && |
1882 | 0 | (node1->schema->flags & LYS_ORDBY_SYSTEM)) { |
1883 | | /* find a matching instance in case they are ordered differently */ |
1884 | 0 | r = lyd_find_sibling_first(node2, node1, (struct lyd_node **)&iter2); |
1885 | 0 | if (r == LY_ENOTFOUND) { |
1886 | | /* no matching instance, data not equal */ |
1887 | 0 | r = LY_ENOT; |
1888 | 0 | } |
1889 | 0 | LY_CHECK_RET(r); |
1890 | 0 | } else { |
1891 | | /* compare with the current node */ |
1892 | 0 | iter2 = node2; |
1893 | 0 | } |
1894 | | |
1895 | | /* data match */ |
1896 | 0 | r = lyd_compare_single_data(node1, iter2, options | LYD_COMPARE_FULL_RECURSION); |
1897 | 0 | LY_CHECK_RET(r); |
1898 | |
|
1899 | 0 | node1 = node1->next; |
1900 | 0 | node2 = node2->next; |
1901 | 0 | } |
1902 | | |
1903 | 0 | return (node1 || node2) ? LY_ENOT : LY_SUCCESS; |
1904 | 0 | } |
1905 | | |
1906 | | LIBYANG_API_DEF LY_ERR |
1907 | | lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) |
1908 | 0 | { |
1909 | 0 | LY_ERR r; |
1910 | |
|
1911 | 0 | if (!node1 || !node2) { |
1912 | 0 | return (node1 == node2) ? LY_SUCCESS : LY_ENOT; |
1913 | 0 | } |
1914 | | |
1915 | | /* schema match */ |
1916 | 0 | if ((r = lyd_compare_single_schema(node1, node2, options, 0))) { |
1917 | 0 | return r; |
1918 | 0 | } |
1919 | | |
1920 | | /* data match */ |
1921 | 0 | return lyd_compare_single_data(node1, node2, options); |
1922 | 0 | } |
1923 | | |
1924 | | LIBYANG_API_DEF LY_ERR |
1925 | | lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) |
1926 | 0 | { |
1927 | 0 | return lyd_compare_siblings_(node1, node2, options, 0); |
1928 | 0 | } |
1929 | | |
1930 | | LIBYANG_API_DEF LY_ERR |
1931 | | lyd_compare_meta(const struct lyd_meta *meta1, const struct lyd_meta *meta2) |
1932 | 0 | { |
1933 | 0 | const struct ly_ctx *ctx; |
1934 | |
|
1935 | 0 | if (!meta1 || !meta2) { |
1936 | 0 | if (meta1 == meta2) { |
1937 | 0 | return LY_SUCCESS; |
1938 | 0 | } else { |
1939 | 0 | return LY_ENOT; |
1940 | 0 | } |
1941 | 0 | } |
1942 | | |
1943 | 0 | ctx = meta1->annotation->module->ctx; |
1944 | 0 | if ((ctx != meta2->annotation->module->ctx) || (meta1->annotation != meta2->annotation)) { |
1945 | 0 | return LY_ENOT; |
1946 | 0 | } |
1947 | | |
1948 | 0 | return LYSC_GET_TYPE_PLG(meta1->value.realtype->plugin_ref)->compare(ctx, &meta1->value, &meta2->value); |
1949 | 0 | } |
1950 | | |
1951 | | /** |
1952 | | * @brief Create a copy of the attribute. |
1953 | | * |
1954 | | * @param[in] attr Attribute to copy. |
1955 | | * @param[in] node Opaque where to append the new attribute. |
1956 | | * @param[out] dup Optional created attribute copy. |
1957 | | * @return LY_ERR value. |
1958 | | */ |
1959 | | static LY_ERR |
1960 | | lyd_dup_attr_single(const struct lyd_attr *attr, struct lyd_node *node, struct lyd_attr **dup) |
1961 | 0 | { |
1962 | 0 | LY_ERR ret = LY_SUCCESS; |
1963 | 0 | struct lyd_attr *a, *last; |
1964 | 0 | struct lyd_node_opaq *opaq = (struct lyd_node_opaq *)node; |
1965 | |
|
1966 | 0 | LY_CHECK_ARG_RET(NULL, attr, node, !node->schema, LY_EINVAL); |
1967 | | |
1968 | | /* create a copy */ |
1969 | 0 | a = calloc(1, sizeof *attr); |
1970 | 0 | LY_CHECK_ERR_RET(!a, LOGMEM(LYD_CTX(node)), LY_EMEM); |
1971 | |
|
1972 | 0 | LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.name, 0, &a->name.name), finish); |
1973 | 0 | LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.prefix, 0, &a->name.prefix), finish); |
1974 | 0 | LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.module_ns, 0, &a->name.module_ns), finish); |
1975 | 0 | LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->value, 0, &a->value), finish); |
1976 | 0 | a->hints = attr->hints; |
1977 | 0 | a->format = attr->format; |
1978 | 0 | if (attr->val_prefix_data) { |
1979 | 0 | ret = ly_dup_prefix_data(LYD_CTX(node), attr->format, attr->val_prefix_data, &a->val_prefix_data); |
1980 | 0 | LY_CHECK_GOTO(ret, finish); |
1981 | 0 | } |
1982 | | |
1983 | | /* insert as the last attribute */ |
1984 | 0 | a->parent = opaq; |
1985 | 0 | if (opaq->attr) { |
1986 | 0 | for (last = opaq->attr; last->next; last = last->next) {} |
1987 | 0 | last->next = a; |
1988 | 0 | } else { |
1989 | 0 | opaq->attr = a; |
1990 | 0 | } |
1991 | |
|
1992 | 0 | finish: |
1993 | 0 | if (ret) { |
1994 | 0 | lyd_free_attr_single(LYD_CTX(node), a); |
1995 | 0 | } else if (dup) { |
1996 | 0 | *dup = a; |
1997 | 0 | } |
1998 | 0 | return LY_SUCCESS; |
1999 | 0 | } |
2000 | | |
2001 | | /** |
2002 | | * @brief Find @p schema equivalent in @p trg_ctx. |
2003 | | * |
2004 | | * @param[in] schema Schema node to find. |
2005 | | * @param[in] trg_ctx Target context to search in. |
2006 | | * @param[in] parent Data parent of @p schema, if any. |
2007 | | * @param[in] log Whether to log directly. |
2008 | | * @param[out] trg_schema Found schema from @p trg_ctx to use. |
2009 | | * @return LY_RRR value. |
2010 | | */ |
2011 | | static LY_ERR |
2012 | | lyd_find_schema_ctx(const struct lysc_node *schema, const struct ly_ctx *trg_ctx, const struct lyd_node *parent, |
2013 | | ly_bool log, const struct lysc_node **trg_schema) |
2014 | 0 | { |
2015 | 0 | const struct lysc_node *src_parent = NULL, *trg_parent = NULL, *sp, *tp; |
2016 | 0 | const struct lys_module *trg_mod = NULL; |
2017 | 0 | char *path; |
2018 | |
|
2019 | 0 | if (!schema) { |
2020 | | /* opaque node */ |
2021 | 0 | *trg_schema = NULL; |
2022 | 0 | return LY_SUCCESS; |
2023 | 0 | } |
2024 | | |
2025 | 0 | if (lysc_data_parent(schema) && parent && parent->schema) { |
2026 | | /* start from schema parent */ |
2027 | 0 | trg_parent = parent->schema; |
2028 | 0 | src_parent = lysc_data_parent(schema); |
2029 | 0 | } |
2030 | |
|
2031 | 0 | do { |
2032 | | /* find the next parent */ |
2033 | 0 | sp = schema; |
2034 | 0 | while (lysc_data_parent(sp) != src_parent) { |
2035 | 0 | sp = lysc_data_parent(sp); |
2036 | 0 | } |
2037 | 0 | src_parent = sp; |
2038 | |
|
2039 | 0 | if (!lysc_data_parent(src_parent)) { |
2040 | | /* find the module first */ |
2041 | 0 | trg_mod = ly_ctx_get_module_implemented(trg_ctx, src_parent->module->name); |
2042 | 0 | if (!trg_mod) { |
2043 | 0 | if (log) { |
2044 | 0 | LOGERR(trg_ctx, LY_ENOTFOUND, "Module \"%s\" not present/implemented in the target context.", |
2045 | 0 | src_parent->module->name); |
2046 | 0 | } |
2047 | 0 | return LY_ENOTFOUND; |
2048 | 0 | } |
2049 | 0 | } |
2050 | | |
2051 | | /* find the next parent */ |
2052 | 0 | assert(trg_parent || trg_mod); |
2053 | 0 | tp = NULL; |
2054 | 0 | while ((tp = lys_getnext(tp, trg_parent, trg_mod ? trg_mod->compiled : NULL, 0))) { |
2055 | 0 | if (!strcmp(tp->name, src_parent->name) && !strcmp(tp->module->name, src_parent->module->name)) { |
2056 | 0 | break; |
2057 | 0 | } |
2058 | 0 | } |
2059 | 0 | if (!tp) { |
2060 | | /* schema node not found */ |
2061 | 0 | if (log) { |
2062 | 0 | path = lysc_path(src_parent, LYSC_PATH_LOG, NULL, 0); |
2063 | 0 | LOGERR(trg_ctx, LY_ENOTFOUND, "Schema node \"%s\" not found in the target context.", path); |
2064 | 0 | free(path); |
2065 | 0 | } |
2066 | 0 | return LY_ENOTFOUND; |
2067 | 0 | } |
2068 | | |
2069 | 0 | trg_parent = tp; |
2070 | 0 | } while (schema != src_parent); |
2071 | | |
2072 | | /* success */ |
2073 | 0 | *trg_schema = trg_parent; |
2074 | 0 | return LY_SUCCESS; |
2075 | 0 | } |
2076 | | |
2077 | | /** |
2078 | | * @brief Return the top-level context of a subtree of node. Handles extension data nodes. |
2079 | | * |
2080 | | * @param[in] node Node to use. |
2081 | | * @return |
2082 | | */ |
2083 | | static const struct ly_ctx * |
2084 | | lyd_dup_get_top_ctx(const struct lyd_node *node) |
2085 | 0 | { |
2086 | 0 | const struct lyd_node *par; |
2087 | |
|
2088 | 0 | par = node; |
2089 | 0 | while (par && !(par->flags & LYD_EXT)) { |
2090 | 0 | par = par->parent; |
2091 | 0 | } |
2092 | |
|
2093 | 0 | if (par && par->parent) { |
2094 | | /* context of the first non-extension parent */ |
2095 | 0 | return LYD_CTX(par->parent); |
2096 | 0 | } |
2097 | | |
2098 | | /* context of the node, all the parents have it */ |
2099 | 0 | return LYD_CTX(node); |
2100 | 0 | } |
2101 | | |
2102 | | /** |
2103 | | * @brief Find (update) the target context for the next node, if needed. |
2104 | | * |
2105 | | * @param[in] orig_node First extension data node being processed from the original tree. |
2106 | | * @param[in] dup_parent Duplicated parent of @p orig_node, set if @p dup_sparent is NULL. |
2107 | | * @param[in] dup_sparent Schema node of the duplicated parent of @p orig_node, set if @p dup_parent is NULL. |
2108 | | * @param[in,out] trg_ctx Target context, may be updated. |
2109 | | * @return LY_ERR value. |
2110 | | */ |
2111 | | static LY_ERR |
2112 | | lyd_find_ext_ctx(const struct lyd_node *orig_node, const struct lyd_node *dup_parent, |
2113 | | const struct lysc_node *dup_sparent, const struct ly_ctx **trg_ctx) |
2114 | 0 | { |
2115 | 0 | LY_ERR r; |
2116 | 0 | const struct lysc_node *snode; |
2117 | 0 | char *path; |
2118 | |
|
2119 | 0 | assert(orig_node && (orig_node->flags & LYD_EXT) && *trg_ctx); |
2120 | | |
2121 | 0 | if (!orig_node->parent) { |
2122 | | /* treat as a non-extension node */ |
2123 | 0 | return LY_SUCCESS; |
2124 | 0 | } |
2125 | 0 | assert(dup_parent || dup_sparent); |
2126 | | |
2127 | 0 | if (LYD_CTX(orig_node->parent) == *trg_ctx) { |
2128 | | /* same contexts, just extension data */ |
2129 | 0 | *trg_ctx = LYD_CTX(orig_node); |
2130 | 0 | return LY_SUCCESS; |
2131 | 0 | } |
2132 | | |
2133 | | /* find the extension context to use from the target context */ |
2134 | 0 | r = lys_find_child_node_ext(*trg_ctx, NULL, dup_parent, dup_sparent, orig_node->schema->module->name, |
2135 | 0 | strlen(orig_node->schema->module->name), LY_VALUE_JSON, NULL, LYD_NAME(orig_node), |
2136 | 0 | strlen(LYD_NAME(orig_node)), 0, &snode, NULL); |
2137 | 0 | if (r == LY_ENOT) { |
2138 | 0 | path = lyd_path(orig_node, LYD_PATH_STD, NULL, 0); |
2139 | 0 | LOGERR(*trg_ctx, LY_ENOTFOUND, "Schema node of an extension node \"%s\" not found in the target context.", path); |
2140 | 0 | free(path); |
2141 | 0 | return LY_ENOTFOUND; |
2142 | 0 | } else if (r) { |
2143 | 0 | return r; |
2144 | 0 | } |
2145 | | |
2146 | | /* update the context */ |
2147 | 0 | *trg_ctx = snode->module->ctx; |
2148 | 0 | return LY_SUCCESS; |
2149 | 0 | } |
2150 | | |
2151 | | /** |
2152 | | * @brief Find (update) the target context for the specific nested node, if needed. |
2153 | | * |
2154 | | * @param[in] orig_node Nested data node being processed from the original tree. |
2155 | | * @param[in,out] trg_ctx Target context, may be updated. |
2156 | | * @return LY_ERR value. |
2157 | | */ |
2158 | | static LY_ERR |
2159 | | lyd_find_ext_ctx_nested(const struct lyd_node *orig_node, const struct ly_ctx **trg_ctx) |
2160 | 0 | { |
2161 | 0 | const struct lyd_node *parent; |
2162 | 0 | const struct lysc_node *sparent; |
2163 | 0 | char *path; |
2164 | |
|
2165 | 0 | if (lyd_dup_get_top_ctx(orig_node) == *trg_ctx) { |
2166 | | /* it is the same context, use the same one for extension data nodes as well (if node is nested in such data) */ |
2167 | 0 | *trg_ctx = LYD_CTX(orig_node); |
2168 | 0 | } else { |
2169 | | /* not the same context, need to find the right one */ |
2170 | 0 | parent = orig_node; |
2171 | 0 | while (parent && !(parent->flags & LYD_EXT)) { |
2172 | 0 | parent = parent->parent; |
2173 | 0 | } |
2174 | |
|
2175 | 0 | if (parent && parent->parent) { |
2176 | | /* find the parent schema node in the target context */ |
2177 | 0 | path = lysc_path(parent->parent->schema, LYSC_PATH_DATA, NULL, 0); |
2178 | 0 | sparent = lys_find_path(*trg_ctx, NULL, path, 0); |
2179 | 0 | if (!sparent) { |
2180 | 0 | LOGERR(*trg_ctx, LY_ENOTFOUND, "Node \"%s\" was not found in the target context.", path); |
2181 | 0 | free(path); |
2182 | 0 | return LY_ENOTFOUND; |
2183 | 0 | } |
2184 | 0 | free(path); |
2185 | |
|
2186 | 0 | LY_CHECK_RET(lyd_find_ext_ctx(parent, NULL, sparent, trg_ctx)); |
2187 | 0 | } |
2188 | 0 | } |
2189 | | |
2190 | 0 | return LY_SUCCESS; |
2191 | 0 | } |
2192 | | |
2193 | | /** |
2194 | | * @brief Duplicate a single node and connect it into @p parent (if present) or last of @p first siblings. |
2195 | | * |
2196 | | * Ignores ::LYD_DUP_WITH_PARENTS which is supposed to be handled by lyd_dup(). |
2197 | | * |
2198 | | * @param[in] node Node to duplicate. |
2199 | | * @param[in] trg_ctx Target context for duplicated nodes. |
2200 | | * @param[in] parent Parent to insert into, NULL for top-level sibling. |
2201 | | * @param[in] insert_order Options for inserting (sorting) duplicated node, @ref insertorder. |
2202 | | * @param[in,out] first First sibling, NULL if no top-level sibling exist yet. Can be also NULL if @p parent is set. |
2203 | | * @param[in] options Bitmask of options flags, see @ref dupoptions. |
2204 | | * @param[out] dup_p Pointer where the created duplicated node is placed (besides connecting it to @p parent / @p first). |
2205 | | * @return LY_ERR value. |
2206 | | */ |
2207 | | static LY_ERR |
2208 | | lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, uint32_t insert_order, |
2209 | | struct lyd_node **first, uint32_t options, struct lyd_node **dup_p) |
2210 | 0 | { |
2211 | 0 | LY_ERR rc = LY_SUCCESS; |
2212 | 0 | struct lyd_node *dup = NULL; |
2213 | 0 | struct lyd_meta *meta; |
2214 | 0 | struct lyd_attr *attr; |
2215 | 0 | struct lyd_node_any *any; |
2216 | 0 | const struct lysc_type *type; |
2217 | 0 | const char *val_can; |
2218 | |
|
2219 | 0 | LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); |
2220 | |
|
2221 | 0 | if (node->flags & LYD_EXT) { |
2222 | 0 | if (options & LYD_DUP_NO_EXT) { |
2223 | | /* no not duplicate this subtree */ |
2224 | 0 | return LY_SUCCESS; |
2225 | 0 | } |
2226 | | |
2227 | 0 | if (parent) { |
2228 | | /* update the context */ |
2229 | 0 | LY_CHECK_GOTO(rc = lyd_find_ext_ctx(node, parent, NULL, &trg_ctx), cleanup); |
2230 | 0 | } /* else called from lyd_dup_get_local_parent() and the context is correct */ |
2231 | 0 | } |
2232 | | |
2233 | 0 | if (!node->schema) { |
2234 | 0 | dup = calloc(1, sizeof(struct lyd_node_opaq)); |
2235 | 0 | ((struct lyd_node_opaq *)dup)->ctx = trg_ctx; |
2236 | 0 | } else { |
2237 | 0 | switch (node->schema->nodetype) { |
2238 | 0 | case LYS_RPC: |
2239 | 0 | case LYS_ACTION: |
2240 | 0 | case LYS_NOTIF: |
2241 | 0 | case LYS_CONTAINER: |
2242 | 0 | case LYS_LIST: |
2243 | 0 | dup = calloc(1, sizeof(struct lyd_node_inner)); |
2244 | 0 | break; |
2245 | 0 | case LYS_LEAF: |
2246 | 0 | case LYS_LEAFLIST: |
2247 | 0 | dup = calloc(1, sizeof(struct lyd_node_term)); |
2248 | 0 | break; |
2249 | 0 | case LYS_ANYDATA: |
2250 | 0 | case LYS_ANYXML: |
2251 | 0 | dup = calloc(1, sizeof(struct lyd_node_any)); |
2252 | 0 | break; |
2253 | 0 | default: |
2254 | 0 | LOGINT(trg_ctx); |
2255 | 0 | rc = LY_EINT; |
2256 | 0 | goto cleanup; |
2257 | 0 | } |
2258 | 0 | } |
2259 | 0 | LY_CHECK_ERR_GOTO(!dup, LOGMEM(trg_ctx); rc = LY_EMEM, cleanup); |
2260 | |
|
2261 | 0 | if (options & LYD_DUP_WITH_FLAGS) { |
2262 | 0 | dup->flags = node->flags; |
2263 | 0 | } else { |
2264 | 0 | dup->flags = (node->flags & (LYD_DEFAULT | LYD_EXT)) | LYD_NEW; |
2265 | 0 | } |
2266 | 0 | if (options & LYD_DUP_WITH_PRIV) { |
2267 | 0 | dup->priv = node->priv; |
2268 | 0 | } |
2269 | | |
2270 | | /* find schema node */ |
2271 | 0 | if (trg_ctx == LYD_CTX(node)) { |
2272 | 0 | dup->schema = node->schema; |
2273 | 0 | } else { |
2274 | 0 | rc = lyd_find_schema_ctx(node->schema, trg_ctx, parent, 1, &dup->schema); |
2275 | 0 | if (rc) { |
2276 | | /* has no schema but is not an opaque node */ |
2277 | 0 | free(dup); |
2278 | 0 | dup = NULL; |
2279 | 0 | goto cleanup; |
2280 | 0 | } |
2281 | 0 | } |
2282 | 0 | dup->prev = dup; |
2283 | | |
2284 | | /* duplicate metadata/attributes */ |
2285 | 0 | if (!(options & LYD_DUP_NO_META)) { |
2286 | 0 | if (!node->schema) { |
2287 | 0 | LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, attr) { |
2288 | 0 | LY_CHECK_GOTO(rc = lyd_dup_attr_single(attr, dup, NULL), cleanup); |
2289 | 0 | } |
2290 | 0 | } else { |
2291 | 0 | LY_LIST_FOR(node->meta, meta) { |
2292 | 0 | LY_CHECK_GOTO(rc = lyd_dup_meta_single_to_ctx(trg_ctx, meta, dup, NULL), cleanup); |
2293 | 0 | } |
2294 | 0 | } |
2295 | 0 | } |
2296 | | |
2297 | | /* nodetype-specific work */ |
2298 | 0 | if (!dup->schema) { |
2299 | 0 | struct lyd_node_opaq *opaq = (struct lyd_node_opaq *)dup; |
2300 | 0 | struct lyd_node_opaq *orig = (struct lyd_node_opaq *)node; |
2301 | 0 | struct lyd_node *child; |
2302 | |
|
2303 | 0 | if (options & LYD_DUP_RECURSIVE) { |
2304 | | /* duplicate all the children */ |
2305 | 0 | LY_LIST_FOR(orig->child, child) { |
2306 | 0 | LY_CHECK_GOTO(rc = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), cleanup); |
2307 | 0 | } |
2308 | 0 | } |
2309 | 0 | LY_CHECK_GOTO(rc = lydict_insert(trg_ctx, orig->name.name, 0, &opaq->name.name), cleanup); |
2310 | 0 | LY_CHECK_GOTO(rc = lydict_insert(trg_ctx, orig->name.prefix, 0, &opaq->name.prefix), cleanup); |
2311 | 0 | LY_CHECK_GOTO(rc = lydict_insert(trg_ctx, orig->name.module_ns, 0, &opaq->name.module_ns), cleanup); |
2312 | 0 | LY_CHECK_GOTO(rc = lydict_insert(trg_ctx, orig->value, 0, &opaq->value), cleanup); |
2313 | 0 | opaq->hints = orig->hints; |
2314 | 0 | opaq->format = orig->format; |
2315 | 0 | if (orig->val_prefix_data) { |
2316 | 0 | rc = ly_dup_prefix_data(trg_ctx, opaq->format, orig->val_prefix_data, &opaq->val_prefix_data); |
2317 | 0 | LY_CHECK_GOTO(rc, cleanup); |
2318 | 0 | } |
2319 | 0 | } else if (dup->schema->nodetype & LYD_NODE_TERM) { |
2320 | 0 | struct lyd_node_term *term = (struct lyd_node_term *)dup; |
2321 | 0 | struct lyd_node_term *orig = (struct lyd_node_term *)node; |
2322 | |
|
2323 | 0 | term->hash = orig->hash; |
2324 | 0 | if (trg_ctx == LYD_CTX(node)) { |
2325 | 0 | rc = LYSC_GET_TYPE_PLG(orig->value.realtype->plugin_ref)->duplicate(trg_ctx, &orig->value, &term->value); |
2326 | 0 | LY_CHECK_ERR_GOTO(rc, LOGERR(trg_ctx, rc, "Value duplication failed."), cleanup); |
2327 | 0 | } else { |
2328 | | /* store canonical value in the target context */ |
2329 | 0 | val_can = lyd_get_value(node); |
2330 | 0 | type = ((struct lysc_node_leaf *)term->schema)->type; |
2331 | 0 | rc = lyd_value_store(trg_ctx, dup, &term->value, type, val_can, strlen(val_can) * 8, 1, 1, NULL, |
2332 | 0 | LY_VALUE_CANON, NULL, LYD_HINT_DATA, term->schema, NULL); |
2333 | 0 | LY_CHECK_GOTO(rc, cleanup); |
2334 | 0 | } |
2335 | 0 | } else if (dup->schema->nodetype & LYD_NODE_INNER) { |
2336 | 0 | struct lyd_node_inner *orig = (struct lyd_node_inner *)node; |
2337 | 0 | struct lyd_node *child; |
2338 | |
|
2339 | 0 | if (options & LYD_DUP_RECURSIVE) { |
2340 | | /* create a hash table with the size of the previous hash table (duplicate) */ |
2341 | 0 | if (orig->children_ht) { |
2342 | 0 | ((struct lyd_node_inner *)dup)->children_ht = lyht_new(orig->children_ht->size, |
2343 | 0 | sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1); |
2344 | 0 | } |
2345 | | |
2346 | | /* duplicate all the children */ |
2347 | 0 | LY_LIST_FOR(orig->child, child) { |
2348 | 0 | LY_CHECK_GOTO(rc = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), cleanup); |
2349 | 0 | } |
2350 | 0 | } else if ((dup->schema->nodetype == LYS_LIST) && !(dup->schema->flags & LYS_KEYLESS)) { |
2351 | | /* always duplicate keys of a list */ |
2352 | 0 | for (child = orig->child; child && lysc_is_key(child->schema); child = child->next) { |
2353 | 0 | LY_CHECK_GOTO(rc = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), cleanup); |
2354 | 0 | } |
2355 | 0 | } |
2356 | 0 | lyd_hash(dup); |
2357 | | |
2358 | | /* NP cont dflt flag */ |
2359 | 0 | lyd_np_cont_dflt_set(dup); |
2360 | 0 | } else if (dup->schema->nodetype & LYD_NODE_ANY) { |
2361 | 0 | dup->hash = node->hash; |
2362 | 0 | any = (struct lyd_node_any *)node; |
2363 | 0 | LY_CHECK_GOTO(rc = lyd_any_copy_value(dup, any->child, any->value, any->hints), cleanup); |
2364 | 0 | ((struct lyd_node_any *)dup)->hints = any->hints; |
2365 | 0 | } |
2366 | | |
2367 | | /* insert */ |
2368 | 0 | lyd_insert_node(parent, first, dup, insert_order); |
2369 | |
|
2370 | 0 | cleanup: |
2371 | 0 | if (rc) { |
2372 | 0 | lyd_free_tree(dup); |
2373 | 0 | } else if (dup_p) { |
2374 | 0 | *dup_p = dup; |
2375 | 0 | } |
2376 | 0 | return rc; |
2377 | 0 | } |
2378 | | |
2379 | | /** |
2380 | | * @brief Get a parent node to connect duplicated subtree to. |
2381 | | * |
2382 | | * @param[in] node Node (subtree) to duplicate. |
2383 | | * @param[in,out] trg_ctx Target context for duplicated nodes, may be updated for @p node. |
2384 | | * @param[in] parent Initial parent to connect to. |
2385 | | * @param[in] options Bitmask of options flags, see @ref dupoptions. |
2386 | | * @param[out] dup_parent First duplicated parent node, if any. |
2387 | | * @param[out] local_parent Correct parent to directly connect duplicated @p node to. |
2388 | | * @return LY_ERR value. |
2389 | | */ |
2390 | | static LY_ERR |
2391 | | lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx **trg_ctx, struct lyd_node *parent, |
2392 | | uint32_t options, struct lyd_node **dup_parent, struct lyd_node **local_parent) |
2393 | 0 | { |
2394 | 0 | const struct lyd_node *orig_parent; |
2395 | 0 | const struct ly_ctx *ctx, *top_ctx; |
2396 | 0 | struct lyd_node *iter = NULL; |
2397 | 0 | ly_bool repeat = 1; |
2398 | |
|
2399 | 0 | assert(node && *trg_ctx); |
2400 | | |
2401 | 0 | *dup_parent = NULL; |
2402 | 0 | *local_parent = NULL; |
2403 | |
|
2404 | 0 | if (!node->parent) { |
2405 | | /* no parents */ |
2406 | 0 | return LY_SUCCESS; |
2407 | 0 | } |
2408 | | |
2409 | | /* adjust the context for node parent correctly */ |
2410 | 0 | top_ctx = *trg_ctx; |
2411 | 0 | LY_CHECK_RET(lyd_find_ext_ctx_nested(node->parent, trg_ctx)); |
2412 | 0 | ctx = *trg_ctx; |
2413 | |
|
2414 | 0 | for (orig_parent = node->parent; repeat && orig_parent; orig_parent = orig_parent->parent) { |
2415 | 0 | if (parent && (LYD_CTX(parent) == LYD_CTX(orig_parent)) && (parent->schema == orig_parent->schema)) { |
2416 | | /* stop creating parents, connect what we have into the provided parent */ |
2417 | 0 | iter = parent; |
2418 | 0 | repeat = 0; |
2419 | 0 | } else if (parent && (LYD_CTX(parent) != LYD_CTX(orig_parent)) && |
2420 | 0 | lyd_compare_schema_equal(parent->schema, orig_parent->schema, 0) && |
2421 | 0 | lyd_compare_schema_parents_equal(parent, orig_parent)) { |
2422 | 0 | iter = parent; |
2423 | 0 | repeat = 0; |
2424 | 0 | } else { |
2425 | 0 | iter = NULL; |
2426 | 0 | LY_CHECK_RET(lyd_dup_r(orig_parent, ctx, NULL, LYD_INSERT_NODE_DEFAULT, &iter, options, &iter)); |
2427 | | |
2428 | | /* insert into the previous duplicated parent */ |
2429 | 0 | if (*dup_parent) { |
2430 | 0 | lyd_insert_node(iter, NULL, *dup_parent, LYD_INSERT_NODE_DEFAULT); |
2431 | 0 | } |
2432 | | |
2433 | | /* update the last duplicated parent */ |
2434 | 0 | *dup_parent = iter; |
2435 | 0 | } |
2436 | | |
2437 | | /* set the first parent */ |
2438 | 0 | if (!*local_parent) { |
2439 | 0 | *local_parent = iter; |
2440 | 0 | } |
2441 | |
|
2442 | 0 | if (orig_parent->flags & LYD_EXT) { |
2443 | | /* parents of the nested extension data, use the original context */ |
2444 | 0 | ctx = top_ctx; |
2445 | 0 | } |
2446 | 0 | } |
2447 | | |
2448 | 0 | if (repeat && parent) { |
2449 | | /* given parent and created parents chain actually do not interconnect */ |
2450 | 0 | LOGERR(*trg_ctx, LY_EINVAL, "None of the duplicated node \"%s\" schema parents match the provided parent \"%s\".", |
2451 | 0 | LYD_NAME(node), LYD_NAME(parent)); |
2452 | 0 | return LY_EINVAL; |
2453 | 0 | } |
2454 | | |
2455 | 0 | if (*dup_parent && parent) { |
2456 | | /* last insert into a prevously-existing parent */ |
2457 | 0 | lyd_insert_node(parent, NULL, *dup_parent, LYD_INSERT_NODE_DEFAULT); |
2458 | 0 | } |
2459 | 0 | return LY_SUCCESS; |
2460 | 0 | } |
2461 | | |
2462 | | static LY_ERR |
2463 | | lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, uint32_t options, |
2464 | | ly_bool nosiblings, struct lyd_node **dup_p) |
2465 | 0 | { |
2466 | 0 | LY_ERR rc; |
2467 | 0 | const struct lyd_node *orig; /* original node to be duplicated */ |
2468 | 0 | struct lyd_node *first_dup = NULL; /* the first duplicated node, this is returned */ |
2469 | 0 | struct lyd_node *top = NULL; /* the most higher created node */ |
2470 | 0 | struct lyd_node *local_parent = NULL; /* the direct parent node for the duplicated node(s) */ |
2471 | 0 | struct lyd_node *dup = NULL; /* duplicate node */ |
2472 | 0 | struct lyd_node *first_sibling = NULL; /* first sibling node */ |
2473 | 0 | const struct lyd_node *first_llist = NULL; /* first duplicated (leaf-)list node, if any */ |
2474 | 0 | uint32_t insert_order; |
2475 | |
|
2476 | 0 | assert(node && trg_ctx); |
2477 | | |
2478 | | /* create/find parents, adjusts the context as well */ |
2479 | 0 | if (options & LYD_DUP_WITH_PARENTS) { |
2480 | 0 | LY_CHECK_GOTO(rc = lyd_dup_get_local_parent(node, &trg_ctx, parent, options & (LYD_DUP_WITH_FLAGS | LYD_DUP_NO_META), |
2481 | 0 | &top, &local_parent), error); |
2482 | 0 | } else { |
2483 | 0 | local_parent = parent; |
2484 | 0 | LY_CHECK_GOTO(rc = lyd_find_ext_ctx_nested(node, &trg_ctx), error); |
2485 | 0 | } |
2486 | | |
2487 | 0 | LY_LIST_FOR(node, orig) { |
2488 | 0 | if (lysc_is_key(orig->schema)) { |
2489 | 0 | if (local_parent) { |
2490 | | /* the key must already exist in the parent */ |
2491 | 0 | rc = lyd_find_sibling_schema(lyd_child(local_parent), orig->schema, &dup); |
2492 | 0 | LY_CHECK_ERR_GOTO(rc, LOGINT(trg_ctx), error); |
2493 | 0 | } else { |
2494 | 0 | assert(!(options & LYD_DUP_WITH_PARENTS)); |
2495 | | /* duplicating a single key, okay, I suppose... */ |
2496 | 0 | rc = lyd_dup_r(orig, trg_ctx, NULL, LYD_INSERT_NODE_DEFAULT, &first_sibling, options, &dup); |
2497 | 0 | LY_CHECK_GOTO(rc, error); |
2498 | 0 | } |
2499 | 0 | } else { |
2500 | | /* decide insert order */ |
2501 | 0 | insert_order = (options & LYD_DUP_NO_LYDS) ? LYD_INSERT_NODE_LAST_BY_SCHEMA : LYD_INSERT_NODE_DEFAULT; |
2502 | 0 | if (first_llist) { |
2503 | 0 | if (orig->schema != first_llist->schema) { |
2504 | | /* all the (leaf-)list instances duplicated */ |
2505 | 0 | first_llist = NULL; |
2506 | 0 | } else { |
2507 | | /* duplicating all the instances of a (leaf-)list, no need to change their order */ |
2508 | 0 | insert_order = LYD_INSERT_NODE_LAST; |
2509 | 0 | } |
2510 | 0 | } else if (orig->schema && (orig->schema->nodetype & (LYS_LIST | LYS_LEAFLIST))) { |
2511 | | /* duplicating the first (leaf-)list instance, duplicate the rest more efficiently */ |
2512 | 0 | first_llist = orig; |
2513 | 0 | } |
2514 | | |
2515 | | /* duplicate the node */ |
2516 | 0 | rc = lyd_dup_r(orig, trg_ctx, local_parent, insert_order, &first_sibling, options, &dup); |
2517 | 0 | LY_CHECK_GOTO(rc, error); |
2518 | |
|
2519 | 0 | if (first_llist && dup->next) { |
2520 | | /* orig was not the last node (because we are inserting into a parent with some previous instances), |
2521 | | * we must check find the order */ |
2522 | 0 | first_llist = NULL; |
2523 | 0 | } |
2524 | 0 | } |
2525 | 0 | first_dup = first_dup ? first_dup : dup; |
2526 | |
|
2527 | 0 | if (nosiblings) { |
2528 | 0 | break; |
2529 | 0 | } |
2530 | 0 | } |
2531 | | |
2532 | 0 | if (dup_p) { |
2533 | 0 | *dup_p = first_dup; |
2534 | 0 | } |
2535 | 0 | return LY_SUCCESS; |
2536 | | |
2537 | 0 | error: |
2538 | 0 | if (top) { |
2539 | 0 | lyd_free_tree(top); |
2540 | 0 | } else if (first_dup) { |
2541 | 0 | lyd_free_siblings(first_dup); |
2542 | 0 | } else { |
2543 | 0 | lyd_free_siblings(dup); |
2544 | 0 | } |
2545 | 0 | return rc; |
2546 | 0 | } |
2547 | | |
2548 | | LIBYANG_API_DEF LY_ERR |
2549 | | lyd_dup_single(const struct lyd_node *node, struct lyd_node *parent, uint32_t options, struct lyd_node **dup) |
2550 | 0 | { |
2551 | 0 | LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); |
2552 | 0 | if (parent && (lyd_dup_get_top_ctx(node) != lyd_dup_get_top_ctx(parent))) { |
2553 | 0 | LOGERR(LYD_CTX(node), LY_EINVAL, "Different \"node\" and \"parent\" contexts used in node duplication."); |
2554 | 0 | return LY_EINVAL; |
2555 | 0 | } |
2556 | | |
2557 | 0 | return lyd_dup(node, lyd_dup_get_top_ctx(node), parent, options, 1, dup); |
2558 | 0 | } |
2559 | | |
2560 | | LIBYANG_API_DEF LY_ERR |
2561 | | lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, |
2562 | | uint32_t options, struct lyd_node **dup) |
2563 | 0 | { |
2564 | 0 | LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL); |
2565 | 0 | if (parent && (trg_ctx != lyd_dup_get_top_ctx(parent))) { |
2566 | 0 | LOGERR(LYD_CTX(node), LY_EINVAL, "Different \"trg_ctx\" and \"parent\" contexts used in node duplication."); |
2567 | 0 | return LY_EINVAL; |
2568 | 0 | } |
2569 | | |
2570 | 0 | return lyd_dup(node, trg_ctx, parent, options, 1, dup); |
2571 | 0 | } |
2572 | | |
2573 | | LIBYANG_API_DEF LY_ERR |
2574 | | lyd_dup_siblings(const struct lyd_node *node, struct lyd_node *parent, uint32_t options, struct lyd_node **dup) |
2575 | 0 | { |
2576 | 0 | LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); |
2577 | 0 | if (parent && (lyd_dup_get_top_ctx(node) != lyd_dup_get_top_ctx(parent))) { |
2578 | 0 | LOGERR(LYD_CTX(node), LY_EINVAL, "Different \"node\" and \"parent\" contexts used in node duplication."); |
2579 | 0 | return LY_EINVAL; |
2580 | 0 | } |
2581 | | |
2582 | 0 | return lyd_dup(node, lyd_dup_get_top_ctx(node), parent, options, 0, dup); |
2583 | 0 | } |
2584 | | |
2585 | | LIBYANG_API_DEF LY_ERR |
2586 | | lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, |
2587 | | uint32_t options, struct lyd_node **dup) |
2588 | 0 | { |
2589 | 0 | LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL); |
2590 | 0 | if (parent && (trg_ctx != lyd_dup_get_top_ctx(parent))) { |
2591 | 0 | LOGERR(LYD_CTX(node), LY_EINVAL, "Different \"trg_ctx\" and \"parent\" contexts used in node duplication."); |
2592 | 0 | return LY_EINVAL; |
2593 | 0 | } |
2594 | | |
2595 | 0 | return lyd_dup(node, trg_ctx, parent, options, 0, dup); |
2596 | 0 | } |
2597 | | |
2598 | | LY_ERR |
2599 | | lyd_dup_meta_single_to_ctx(const struct ly_ctx *parent_ctx, const struct lyd_meta *meta, struct lyd_node *parent, |
2600 | | struct lyd_meta **dup) |
2601 | 0 | { |
2602 | 0 | LY_ERR ret = LY_SUCCESS; |
2603 | 0 | struct lyd_meta *mt, *last; |
2604 | 0 | const struct lysc_type *ant_type; |
2605 | 0 | struct lys_module *mod; |
2606 | 0 | const char *val_can; |
2607 | |
|
2608 | 0 | LY_CHECK_ARG_RET(NULL, meta, parent, LY_EINVAL); |
2609 | | |
2610 | | /* create a copy */ |
2611 | 0 | mt = calloc(1, sizeof *mt); |
2612 | 0 | LY_CHECK_ERR_RET(!mt, LOGMEM(LYD_CTX(parent)), LY_EMEM); |
2613 | |
|
2614 | 0 | if (parent_ctx != meta->annotation->module->ctx) { |
2615 | | /* different contexts */ |
2616 | 0 | mod = ly_ctx_get_module(parent_ctx, meta->annotation->module->name, meta->annotation->module->revision); |
2617 | | |
2618 | | /* annotation */ |
2619 | 0 | mt->annotation = lyd_get_meta_annotation(mod, meta->name, strlen(meta->name)); |
2620 | 0 | lyplg_ext_get_storage(mt->annotation, LY_STMT_TYPE, sizeof ant_type, (const void **)&ant_type); |
2621 | 0 | LY_CHECK_ERR_GOTO((ret = mt->annotation ? LY_SUCCESS : LY_EINVAL), LOGERR(parent_ctx, LY_EINVAL, |
2622 | 0 | "Annotation for metadata %s not found, value duplication failed.", meta->name), finish); |
2623 | | |
2624 | | /* duplicate callback expect only the same contexts, so use the store callback */ |
2625 | 0 | val_can = lyd_value_get_canonical(meta->annotation->module->ctx, &meta->value); |
2626 | 0 | ret = lyd_value_store(parent_ctx, parent, &mt->value, ant_type, val_can, strlen(val_can) * 8, 1, 1, NULL, |
2627 | 0 | LY_VALUE_CANON, NULL, LYD_HINT_DATA, parent->schema, NULL); |
2628 | 0 | } else { |
2629 | | /* annotation */ |
2630 | 0 | mt->annotation = meta->annotation; |
2631 | | /* duplication of value */ |
2632 | 0 | ret = LYSC_GET_TYPE_PLG(meta->value.realtype->plugin_ref)->duplicate(parent_ctx, &meta->value, &mt->value); |
2633 | 0 | } |
2634 | 0 | LY_CHECK_ERR_GOTO(ret, LOGERR(LYD_CTX(parent), LY_EINT, "Value duplication failed."), finish); |
2635 | 0 | LY_CHECK_GOTO(ret = lydict_insert(parent_ctx, meta->name, 0, &mt->name), finish); |
2636 | | |
2637 | | /* insert as the last attribute */ |
2638 | 0 | mt->parent = parent; |
2639 | 0 | if (parent->meta) { |
2640 | 0 | for (last = parent->meta; last->next; last = last->next) {} |
2641 | 0 | last->next = mt; |
2642 | 0 | } else { |
2643 | 0 | parent->meta = mt; |
2644 | 0 | } |
2645 | |
|
2646 | 0 | finish: |
2647 | 0 | if (ret) { |
2648 | 0 | lyd_free_meta_single(mt); |
2649 | 0 | } else if (dup) { |
2650 | 0 | *dup = mt; |
2651 | 0 | } |
2652 | 0 | return LY_SUCCESS; |
2653 | 0 | } |
2654 | | |
2655 | | LIBYANG_API_DEF LY_ERR |
2656 | | lyd_dup_meta_single(const struct lyd_meta *meta, struct lyd_node *node, struct lyd_meta **dup) |
2657 | 0 | { |
2658 | 0 | LY_CHECK_ARG_RET(NULL, meta, LY_EINVAL); |
2659 | | |
2660 | | /* log to node context but value must always use the annotation context */ |
2661 | 0 | return lyd_dup_meta_single_to_ctx(meta->annotation->module->ctx, meta, node, dup); |
2662 | 0 | } |
2663 | | |
2664 | | /** |
2665 | | * @brief Merge a source sibling into target siblings. |
2666 | | * |
2667 | | * @param[in,out] first_trg First target sibling, is updated if top-level. |
2668 | | * @param[in] parent_trg Target parent. |
2669 | | * @param[in,out] sibling_src_p Source sibling to merge, set to NULL if spent. |
2670 | | * @param[in] merge_cb Optional merge callback. |
2671 | | * @param[in] cb_data Arbitrary callback data. |
2672 | | * @param[in] options Merge options. |
2673 | | * @param[in] lyds Pool of lyds data which can be reused. |
2674 | | * @param[in,out] leader_p Cached first instance of target (leaf-)list. |
2675 | | * @param[in,out] dup_inst Duplicate instance cache for all @p first_trg siblings. |
2676 | | * @return LY_ERR value. |
2677 | | */ |
2678 | | static LY_ERR |
2679 | | lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg, |
2680 | | const struct lyd_node **sibling_src_p, lyd_merge_cb merge_cb, void *cb_data, uint16_t options, |
2681 | | struct lyds_pool *lyds, struct lyd_node **leader_p, struct ly_ht **dup_inst) |
2682 | 0 | { |
2683 | 0 | const struct lyd_node *child_src, *tmp, *sibling_src; |
2684 | 0 | struct lyd_node *match_trg, *dup_src, *elem, *leader; |
2685 | 0 | struct lyd_node_opaq *opaq_trg, *opaq_src; |
2686 | 0 | const struct lyd_node_any *any; |
2687 | 0 | const struct lysc_node *schema; |
2688 | 0 | struct ly_ht *child_dup_inst = NULL; |
2689 | 0 | LY_ERR r; |
2690 | 0 | ly_bool first_inst = 0; |
2691 | |
|
2692 | 0 | sibling_src = *sibling_src_p; |
2693 | 0 | if (!sibling_src->schema) { |
2694 | | /* try to find the same opaque node */ |
2695 | 0 | r = lyd_find_sibling_opaq_next(*first_trg, LYD_NAME(sibling_src), &match_trg); |
2696 | 0 | } else if (sibling_src->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) { |
2697 | | /* try to find the exact instance */ |
2698 | 0 | r = lyd_find_sibling_first(*first_trg, sibling_src, &match_trg); |
2699 | 0 | } else { |
2700 | | /* try to simply find the node, there cannot be more instances */ |
2701 | 0 | r = lyd_find_sibling_val(*first_trg, sibling_src->schema, NULL, 0, &match_trg); |
2702 | 0 | } |
2703 | 0 | LY_CHECK_RET(r && (r != LY_ENOTFOUND), r); |
2704 | |
|
2705 | 0 | if (match_trg) { |
2706 | | /* update match as needed */ |
2707 | 0 | LY_CHECK_RET(lyd_dup_inst_next(&match_trg, dup_inst)); |
2708 | 0 | } else { |
2709 | | /* first instance of this node */ |
2710 | 0 | first_inst = 1; |
2711 | 0 | } |
2712 | | |
2713 | 0 | if (match_trg) { |
2714 | | /* call callback */ |
2715 | 0 | if (merge_cb) { |
2716 | 0 | LY_CHECK_RET(merge_cb(match_trg, sibling_src, cb_data)); |
2717 | 0 | } |
2718 | | |
2719 | | /* node found, make sure even value matches for all node types */ |
2720 | 0 | if (!match_trg->schema) { |
2721 | 0 | if (lyd_compare_single(sibling_src, match_trg, 0)) { |
2722 | | /* update value */ |
2723 | 0 | opaq_trg = (struct lyd_node_opaq *)match_trg; |
2724 | 0 | opaq_src = (struct lyd_node_opaq *)sibling_src; |
2725 | |
|
2726 | 0 | lydict_remove(LYD_CTX(opaq_trg), opaq_trg->value); |
2727 | 0 | lydict_insert(LYD_CTX(opaq_trg), opaq_src->value, 0, &opaq_trg->value); |
2728 | 0 | opaq_trg->hints = opaq_src->hints; |
2729 | |
|
2730 | 0 | ly_free_prefix_data(opaq_trg->format, opaq_trg->val_prefix_data); |
2731 | 0 | opaq_trg->format = opaq_src->format; |
2732 | 0 | ly_dup_prefix_data(LYD_CTX(opaq_trg), opaq_src->format, opaq_src->val_prefix_data, |
2733 | 0 | &opaq_trg->val_prefix_data); |
2734 | 0 | } |
2735 | 0 | } else if ((match_trg->schema->nodetype == LYS_LEAF) && |
2736 | 0 | ((options & LYD_MERGE_DEFAULTS) || !(sibling_src->flags & LYD_DEFAULT))) { |
2737 | | /* update value */ |
2738 | 0 | r = lyd_change_term_val(match_trg, &((struct lyd_node_term *)sibling_src)->value, 0, |
2739 | 0 | sibling_src->flags & LYD_DEFAULT); |
2740 | 0 | LY_CHECK_RET(r && (r != LY_EEXIST) && (r != LY_ENOT), r); |
2741 | |
|
2742 | 0 | if (options & LYD_MERGE_WITH_FLAGS) { |
2743 | | /* keep the exact same flags */ |
2744 | 0 | match_trg->flags = sibling_src->flags; |
2745 | 0 | } |
2746 | 0 | } else if ((match_trg->schema->nodetype & LYS_ANYDATA) && lyd_compare_single(sibling_src, match_trg, 0)) { |
2747 | | /* update value */ |
2748 | 0 | any = (const struct lyd_node_any *)sibling_src; |
2749 | 0 | LY_CHECK_RET(lyd_any_copy_value(match_trg, any->child, any->value, any->hints)); |
2750 | | |
2751 | | /* copy flags and add LYD_NEW */ |
2752 | 0 | match_trg->flags = sibling_src->flags | ((options & LYD_MERGE_WITH_FLAGS) ? 0 : LYD_NEW); |
2753 | 0 | } |
2754 | | |
2755 | | /* check descendants, recursively */ |
2756 | 0 | r = LY_SUCCESS; |
2757 | 0 | leader = NULL; |
2758 | 0 | schema = NULL; |
2759 | 0 | LY_LIST_FOR_SAFE(lyd_child_no_keys(sibling_src), tmp, child_src) { |
2760 | 0 | if ((options & LYD_MERGE_DESTRUCT) && (schema != child_src->schema) && LYDS_NODE_IS_LEADER(child_src)) { |
2761 | 0 | schema = child_src->schema; |
2762 | | /* unlink lyds data and add them to the pool */ |
2763 | 0 | lyds_pool_add((struct lyd_node *)child_src, lyds); |
2764 | 0 | } |
2765 | |
|
2766 | 0 | r = lyd_merge_sibling_r(lyd_node_child_p(match_trg), match_trg, &child_src, |
2767 | 0 | merge_cb, cb_data, options, lyds, &leader, &child_dup_inst); |
2768 | 0 | if (r) { |
2769 | 0 | break; |
2770 | 0 | } |
2771 | 0 | } |
2772 | |
|
2773 | 0 | lyd_dup_inst_free(child_dup_inst); |
2774 | 0 | LY_CHECK_RET(r); |
2775 | 0 | } else { |
2776 | | /* node not found, merge it */ |
2777 | 0 | if (options & LYD_MERGE_DESTRUCT) { |
2778 | 0 | dup_src = (struct lyd_node *)sibling_src; |
2779 | 0 | lyd_unlink_ignore_lyds(NULL, dup_src); |
2780 | | /* spend it */ |
2781 | 0 | *sibling_src_p = NULL; |
2782 | 0 | } else { |
2783 | 0 | LY_CHECK_RET(lyd_dup_single(sibling_src, NULL, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &dup_src)); |
2784 | 0 | } |
2785 | | |
2786 | 0 | if (!(options & LYD_MERGE_WITH_FLAGS)) { |
2787 | | /* set LYD_NEW for all the new nodes, required for validation */ |
2788 | 0 | LYD_TREE_DFS_BEGIN(dup_src, elem) { |
2789 | 0 | elem->flags |= LYD_NEW; |
2790 | 0 | LYD_TREE_DFS_END(dup_src, elem); |
2791 | 0 | } |
2792 | 0 | } |
2793 | |
|
2794 | 0 | if (lyds->rbn) { |
2795 | | /* insert node and try to reuse free lyds data */ |
2796 | 0 | lyds_insert2(parent_trg, first_trg, leader_p, dup_src, lyds); |
2797 | 0 | } else { |
2798 | | /* generic insert node */ |
2799 | 0 | lyd_insert_node(parent_trg, first_trg, dup_src, LYD_INSERT_NODE_DEFAULT); |
2800 | 0 | } |
2801 | |
|
2802 | 0 | if (first_inst) { |
2803 | | /* remember not to find this instance next time */ |
2804 | 0 | LY_CHECK_RET(lyd_dup_inst_next(&dup_src, dup_inst)); |
2805 | 0 | } |
2806 | | |
2807 | | /* call callback, no source node */ |
2808 | 0 | if (merge_cb) { |
2809 | 0 | LY_CHECK_RET(merge_cb(dup_src, NULL, cb_data)); |
2810 | 0 | } |
2811 | 0 | } |
2812 | | |
2813 | 0 | return LY_SUCCESS; |
2814 | 0 | } |
2815 | | |
2816 | | static LY_ERR |
2817 | | lyd_merge(struct lyd_node **target, const struct lyd_node *source, const struct lys_module *mod, |
2818 | | lyd_merge_cb merge_cb, void *cb_data, uint16_t options, ly_bool nosiblings) |
2819 | 0 | { |
2820 | 0 | const struct lyd_node *sibling_src, *tmp; |
2821 | 0 | const struct lysc_node *schema; |
2822 | 0 | struct lyd_node *leader; |
2823 | 0 | struct ly_ht *dup_inst = NULL; |
2824 | 0 | ly_bool first; |
2825 | 0 | LY_ERR ret = LY_SUCCESS; |
2826 | 0 | struct lyds_pool lyds = {0}; |
2827 | |
|
2828 | 0 | LY_CHECK_ARG_RET(NULL, target, LY_EINVAL); |
2829 | 0 | LY_CHECK_CTX_EQUAL_RET(__func__, *target ? LYD_CTX(*target) : NULL, source ? LYD_CTX(source) : NULL, |
2830 | 0 | mod ? mod->ctx : NULL, LY_EINVAL); |
2831 | |
|
2832 | 0 | if (!source) { |
2833 | | /* nothing to merge */ |
2834 | 0 | return LY_SUCCESS; |
2835 | 0 | } |
2836 | | |
2837 | 0 | if ((*target && lysc_data_parent((*target)->schema)) || lysc_data_parent(source->schema)) { |
2838 | 0 | LOGERR(LYD_CTX(source), LY_EINVAL, "Invalid arguments - can merge only 2 top-level subtrees (%s()).", __func__); |
2839 | 0 | return LY_EINVAL; |
2840 | 0 | } |
2841 | | |
2842 | 0 | leader = NULL; |
2843 | 0 | schema = NULL; |
2844 | 0 | LY_LIST_FOR_SAFE(source, tmp, sibling_src) { |
2845 | 0 | if (mod && (lyd_owner_module(sibling_src) != mod)) { |
2846 | | /* skip data nodes from different modules */ |
2847 | 0 | continue; |
2848 | 0 | } |
2849 | | |
2850 | 0 | if ((options & LYD_MERGE_DESTRUCT) && (schema != sibling_src->schema) && LYDS_NODE_IS_LEADER(sibling_src)) { |
2851 | 0 | schema = sibling_src->schema; |
2852 | | /* unlink lyds data and add them to the pool */ |
2853 | 0 | lyds_pool_add((struct lyd_node *)sibling_src, &lyds); |
2854 | 0 | } |
2855 | |
|
2856 | 0 | first = (sibling_src == source) ? 1 : 0; |
2857 | 0 | ret = lyd_merge_sibling_r(target, NULL, &sibling_src, merge_cb, cb_data, options, |
2858 | 0 | &lyds, &leader, &dup_inst); |
2859 | 0 | if (ret) { |
2860 | 0 | break; |
2861 | 0 | } |
2862 | 0 | if (first && !sibling_src) { |
2863 | | /* source was spent (unlinked), move to the next node */ |
2864 | 0 | source = tmp; |
2865 | 0 | } |
2866 | |
|
2867 | 0 | if (nosiblings) { |
2868 | 0 | break; |
2869 | 0 | } |
2870 | 0 | } |
2871 | 0 | lyds_pool_clean(&lyds); |
2872 | |
|
2873 | 0 | if (options & LYD_MERGE_DESTRUCT) { |
2874 | | /* free any leftover source data that were not merged */ |
2875 | 0 | lyd_free_siblings((struct lyd_node *)source); |
2876 | 0 | } |
2877 | |
|
2878 | 0 | lyd_dup_inst_free(dup_inst); |
2879 | 0 | return ret; |
2880 | 0 | } |
2881 | | |
2882 | | LIBYANG_API_DEF LY_ERR |
2883 | | lyd_merge_tree(struct lyd_node **target, const struct lyd_node *source, uint16_t options) |
2884 | 0 | { |
2885 | 0 | return lyd_merge(target, source, NULL, NULL, NULL, options, 1); |
2886 | 0 | } |
2887 | | |
2888 | | LIBYANG_API_DEF LY_ERR |
2889 | | lyd_merge_siblings(struct lyd_node **target, const struct lyd_node *source, uint16_t options) |
2890 | 0 | { |
2891 | 0 | return lyd_merge(target, source, NULL, NULL, NULL, options, 0); |
2892 | 0 | } |
2893 | | |
2894 | | LIBYANG_API_DEF LY_ERR |
2895 | | lyd_merge_module(struct lyd_node **target, const struct lyd_node *source, const struct lys_module *mod, |
2896 | | lyd_merge_cb merge_cb, void *cb_data, uint16_t options) |
2897 | 0 | { |
2898 | 0 | return lyd_merge(target, source, mod, merge_cb, cb_data, options, 0); |
2899 | 0 | } |
2900 | | |
2901 | | static LY_ERR |
2902 | | lyd_path_str_enlarge(char **buffer, size_t *buflen, size_t reqlen, ly_bool is_static) |
2903 | 0 | { |
2904 | | /* ending \0 */ |
2905 | 0 | ++reqlen; |
2906 | |
|
2907 | 0 | if (reqlen > *buflen) { |
2908 | 0 | if (is_static) { |
2909 | 0 | return LY_EINCOMPLETE; |
2910 | 0 | } |
2911 | | |
2912 | 0 | *buffer = ly_realloc(*buffer, reqlen * sizeof **buffer); |
2913 | 0 | if (!*buffer) { |
2914 | 0 | return LY_EMEM; |
2915 | 0 | } |
2916 | | |
2917 | 0 | *buflen = reqlen; |
2918 | 0 | } |
2919 | | |
2920 | 0 | return LY_SUCCESS; |
2921 | 0 | } |
2922 | | |
2923 | | LY_ERR |
2924 | | lyd_path_list_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static) |
2925 | 0 | { |
2926 | 0 | const struct lyd_node *key; |
2927 | 0 | size_t len; |
2928 | 0 | const char *val; |
2929 | 0 | char quot; |
2930 | |
|
2931 | 0 | for (key = lyd_child(node); key && key->schema && (key->schema->flags & LYS_KEY); key = key->next) { |
2932 | 0 | val = lyd_get_value(key); |
2933 | 0 | len = 1 + strlen(key->schema->name) + 2 + strlen(val) + 2; |
2934 | 0 | LY_CHECK_RET(lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static)); |
2935 | |
|
2936 | 0 | LY_CHECK_RET(ly_val_get_quot(LYD_CTX(node), val, ")); |
2937 | 0 | *bufused += sprintf(*buffer + *bufused, "[%s=%c%s%c]", key->schema->name, quot, val, quot); |
2938 | 0 | } |
2939 | | |
2940 | 0 | return LY_SUCCESS; |
2941 | 0 | } |
2942 | | |
2943 | | /** |
2944 | | * @brief Append leaf-list value predicate to path. |
2945 | | * |
2946 | | * @param[in] node Node to print. |
2947 | | * @param[in,out] buffer Buffer to print to. |
2948 | | * @param[in,out] buflen Current buffer length. |
2949 | | * @param[in,out] bufused Current number of characters used in @p buffer. |
2950 | | * @param[in] is_static Whether buffer is static or can be reallocated. |
2951 | | * @return LY_ERR value. |
2952 | | */ |
2953 | | static LY_ERR |
2954 | | lyd_path_leaflist_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static) |
2955 | 0 | { |
2956 | 0 | size_t len; |
2957 | 0 | const char *val; |
2958 | 0 | char quot; |
2959 | |
|
2960 | 0 | val = lyd_get_value(node); |
2961 | 0 | len = 4 + strlen(val) + 2; /* "[.='" + val + "']" */ |
2962 | 0 | LY_CHECK_RET(lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static)); |
2963 | |
|
2964 | 0 | LY_CHECK_RET(ly_val_get_quot(LYD_CTX(node), val, ")); |
2965 | 0 | *bufused += sprintf(*buffer + *bufused, "[.=%c%s%c]", quot, val, quot); |
2966 | |
|
2967 | 0 | return LY_SUCCESS; |
2968 | 0 | } |
2969 | | |
2970 | | /** |
2971 | | * @brief Append node position (relative to its other instances) predicate to path. |
2972 | | * |
2973 | | * @param[in] node Node to print. |
2974 | | * @param[in,out] buffer Buffer to print to. |
2975 | | * @param[in,out] buflen Current buffer length. |
2976 | | * @param[in,out] bufused Current number of characters used in @p buffer. |
2977 | | * @param[in] is_static Whether buffer is static or can be reallocated. |
2978 | | * @return LY_ERR |
2979 | | */ |
2980 | | static LY_ERR |
2981 | | lyd_path_position_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static) |
2982 | 0 | { |
2983 | 0 | size_t len; |
2984 | 0 | uint32_t pos; |
2985 | 0 | char *val = NULL; |
2986 | 0 | LY_ERR rc; |
2987 | |
|
2988 | 0 | pos = lyd_list_pos(node); |
2989 | 0 | if (asprintf(&val, "%" PRIu32, pos) == -1) { |
2990 | 0 | return LY_EMEM; |
2991 | 0 | } |
2992 | | |
2993 | 0 | len = 1 + strlen(val) + 1; |
2994 | 0 | rc = lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static); |
2995 | 0 | if (rc != LY_SUCCESS) { |
2996 | 0 | goto cleanup; |
2997 | 0 | } |
2998 | | |
2999 | 0 | *bufused += sprintf(*buffer + *bufused, "[%s]", val); |
3000 | |
|
3001 | 0 | cleanup: |
3002 | 0 | free(val); |
3003 | 0 | return rc; |
3004 | 0 | } |
3005 | | |
3006 | | LIBYANG_API_DEF char * |
3007 | | lyd_path(const struct lyd_node *node, LYD_PATH_TYPE pathtype, char *buffer, size_t buflen) |
3008 | 0 | { |
3009 | 0 | ly_bool is_static = 0; |
3010 | 0 | uint32_t i, depth; |
3011 | 0 | size_t bufused = 0, len; |
3012 | 0 | const struct lyd_node *iter, *parent; |
3013 | 0 | const struct lys_module *mod, *prev_mod; |
3014 | 0 | LY_ERR rc = LY_SUCCESS; |
3015 | |
|
3016 | 0 | LY_CHECK_ARG_RET(NULL, node, NULL); |
3017 | 0 | if (buffer) { |
3018 | 0 | LY_CHECK_ARG_RET(LYD_CTX(node), buflen > 1, NULL); |
3019 | 0 | is_static = 1; |
3020 | 0 | } else { |
3021 | 0 | buflen = 0; |
3022 | 0 | } |
3023 | | |
3024 | 0 | switch (pathtype) { |
3025 | 0 | case LYD_PATH_STD: |
3026 | 0 | case LYD_PATH_STD_NO_LAST_PRED: |
3027 | 0 | depth = 1; |
3028 | 0 | for (iter = node; iter->parent; iter = iter->parent) { |
3029 | 0 | ++depth; |
3030 | 0 | } |
3031 | |
|
3032 | 0 | goto iter_print; |
3033 | 0 | while (depth) { |
3034 | | /* find the right node */ |
3035 | 0 | for (iter = node, i = 1; i < depth; iter = iter->parent, ++i) {} |
3036 | 0 | iter_print: |
3037 | | /* get the module */ |
3038 | 0 | mod = lyd_node_module(iter); |
3039 | 0 | parent = iter->parent; |
3040 | 0 | prev_mod = lyd_node_module(parent); |
3041 | 0 | if (prev_mod == mod) { |
3042 | 0 | mod = NULL; |
3043 | 0 | } |
3044 | | |
3045 | | /* realloc string */ |
3046 | 0 | len = 1 + (mod ? strlen(mod->name) + 1 : 0) + (iter->schema ? strlen(iter->schema->name) : |
3047 | 0 | strlen(((struct lyd_node_opaq *)iter)->name.name)); |
3048 | 0 | rc = lyd_path_str_enlarge(&buffer, &buflen, bufused + len, is_static); |
3049 | 0 | if (rc) { |
3050 | 0 | break; |
3051 | 0 | } |
3052 | | |
3053 | | /* print next node */ |
3054 | 0 | bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", LYD_NAME(iter)); |
3055 | | |
3056 | | /* do not always print the last (first) predicate */ |
3057 | 0 | if (iter->schema && ((depth > 1) || (pathtype == LYD_PATH_STD))) { |
3058 | 0 | switch (iter->schema->nodetype) { |
3059 | 0 | case LYS_LIST: |
3060 | 0 | if (iter->schema->flags & LYS_KEYLESS) { |
3061 | | /* print its position */ |
3062 | 0 | rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, is_static); |
3063 | 0 | } else { |
3064 | | /* print all list keys in predicates */ |
3065 | 0 | rc = lyd_path_list_predicate(iter, &buffer, &buflen, &bufused, is_static); |
3066 | 0 | } |
3067 | 0 | break; |
3068 | 0 | case LYS_LEAFLIST: |
3069 | 0 | if (iter->schema->flags & LYS_CONFIG_W) { |
3070 | | /* print leaf-list value */ |
3071 | 0 | rc = lyd_path_leaflist_predicate(iter, &buffer, &buflen, &bufused, is_static); |
3072 | 0 | } else { |
3073 | | /* print its position */ |
3074 | 0 | rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, is_static); |
3075 | 0 | } |
3076 | 0 | break; |
3077 | 0 | default: |
3078 | | /* nothing to print more */ |
3079 | 0 | break; |
3080 | 0 | } |
3081 | 0 | } |
3082 | 0 | if (rc) { |
3083 | 0 | break; |
3084 | 0 | } |
3085 | | |
3086 | 0 | --depth; |
3087 | 0 | } |
3088 | 0 | break; |
3089 | 0 | } |
3090 | | |
3091 | 0 | if (rc && !is_static) { |
3092 | 0 | free(buffer); |
3093 | 0 | buffer = NULL; |
3094 | 0 | } |
3095 | 0 | return buffer; |
3096 | 0 | } |
3097 | | |
3098 | | char * |
3099 | | lyd_path_set(const struct ly_set *dnodes, LYD_PATH_TYPE pathtype) |
3100 | 0 | { |
3101 | 0 | uint32_t depth; |
3102 | 0 | size_t bufused = 0, buflen = 0, len; |
3103 | 0 | char *buffer = NULL; |
3104 | 0 | const struct lyd_node *iter, *parent; |
3105 | 0 | const struct lys_module *mod, *prev_mod; |
3106 | 0 | LY_ERR rc = LY_SUCCESS; |
3107 | |
|
3108 | 0 | switch (pathtype) { |
3109 | 0 | case LYD_PATH_STD: |
3110 | 0 | case LYD_PATH_STD_NO_LAST_PRED: |
3111 | 0 | for (depth = 1; depth <= dnodes->count; ++depth) { |
3112 | | /* current node */ |
3113 | 0 | iter = dnodes->dnodes[depth - 1]; |
3114 | 0 | mod = lyd_node_module(iter); |
3115 | | |
3116 | | /* parent */ |
3117 | 0 | parent = (depth > 1) ? dnodes->dnodes[depth - 2] : NULL; |
3118 | 0 | assert(!parent || !iter->schema || !parent->schema || (parent->schema->nodetype & LYD_NODE_ANY) || |
3119 | 0 | (lysc_data_parent(iter->schema) == parent->schema) || !lysc_data_parent(iter->schema) || |
3120 | 0 | (parent->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))); |
3121 | | |
3122 | | /* get module to print, if any */ |
3123 | 0 | prev_mod = lyd_node_module(parent); |
3124 | 0 | if (prev_mod == mod) { |
3125 | 0 | mod = NULL; |
3126 | 0 | } |
3127 | | |
3128 | | /* realloc string */ |
3129 | 0 | len = 1 + (mod ? strlen(mod->name) + 1 : 0) + (iter->schema ? strlen(iter->schema->name) : |
3130 | 0 | strlen(((struct lyd_node_opaq *)iter)->name.name)); |
3131 | 0 | if ((rc = lyd_path_str_enlarge(&buffer, &buflen, bufused + len, 0))) { |
3132 | 0 | break; |
3133 | 0 | } |
3134 | | |
3135 | | /* print next node */ |
3136 | 0 | bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", LYD_NAME(iter)); |
3137 | | |
3138 | | /* do not always print the last (first) predicate */ |
3139 | 0 | if (iter->schema && ((depth > 1) || (pathtype == LYD_PATH_STD))) { |
3140 | 0 | switch (iter->schema->nodetype) { |
3141 | 0 | case LYS_LIST: |
3142 | 0 | if (iter->schema->flags & LYS_KEYLESS) { |
3143 | | /* print its position */ |
3144 | 0 | rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, 0); |
3145 | 0 | } else { |
3146 | | /* print all list keys in predicates */ |
3147 | 0 | rc = lyd_path_list_predicate(iter, &buffer, &buflen, &bufused, 0); |
3148 | 0 | } |
3149 | 0 | break; |
3150 | 0 | case LYS_LEAFLIST: |
3151 | 0 | if (iter->schema->flags & LYS_CONFIG_W) { |
3152 | | /* print leaf-list value */ |
3153 | 0 | rc = lyd_path_leaflist_predicate(iter, &buffer, &buflen, &bufused, 0); |
3154 | 0 | } else { |
3155 | | /* print its position */ |
3156 | 0 | rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, 0); |
3157 | 0 | } |
3158 | 0 | break; |
3159 | 0 | default: |
3160 | | /* nothing to print more */ |
3161 | 0 | break; |
3162 | 0 | } |
3163 | 0 | } |
3164 | 0 | if (rc) { |
3165 | 0 | break; |
3166 | 0 | } |
3167 | 0 | } |
3168 | 0 | break; |
3169 | 0 | } |
3170 | | |
3171 | 0 | return buffer; |
3172 | 0 | } |
3173 | | |
3174 | | LIBYANG_API_DEF struct lyd_meta * |
3175 | | lyd_find_meta(const struct lyd_meta *first, const struct lys_module *module, const char *name) |
3176 | 0 | { |
3177 | 0 | struct lyd_meta *ret = NULL; |
3178 | 0 | const struct ly_ctx *ctx; |
3179 | 0 | const char *prefix, *tmp; |
3180 | 0 | char *str; |
3181 | 0 | uint32_t pref_len, name_len; |
3182 | |
|
3183 | 0 | LY_CHECK_ARG_RET(NULL, module || strchr(name, ':'), name, NULL); |
3184 | 0 | LY_CHECK_CTX_EQUAL_RET(__func__, first ? first->annotation->module->ctx : NULL, module ? module->ctx : NULL, NULL); |
3185 | |
|
3186 | 0 | if (!first) { |
3187 | 0 | return NULL; |
3188 | 0 | } |
3189 | | |
3190 | 0 | ctx = first->annotation->module->ctx; |
3191 | | |
3192 | | /* parse the name */ |
3193 | 0 | tmp = name; |
3194 | 0 | if (ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len) || tmp[0]) { |
3195 | 0 | LOGERR(ctx, LY_EINVAL, "Metadata name \"%s\" is not valid.", name); |
3196 | 0 | return NULL; |
3197 | 0 | } |
3198 | | |
3199 | | /* find the module */ |
3200 | 0 | if (prefix) { |
3201 | 0 | str = strndup(prefix, pref_len); |
3202 | 0 | module = ly_ctx_get_module_latest(ctx, str); |
3203 | 0 | free(str); |
3204 | 0 | LY_CHECK_ERR_RET(!module, LOGERR(ctx, LY_EINVAL, "Module \"%.*s\" not found.", (int)pref_len, prefix), NULL); |
3205 | 0 | } |
3206 | | |
3207 | | /* find the metadata */ |
3208 | 0 | LY_LIST_FOR(first, first) { |
3209 | 0 | if ((first->annotation->module == module) && !strcmp(first->name, name)) { |
3210 | 0 | ret = (struct lyd_meta *)first; |
3211 | 0 | break; |
3212 | 0 | } |
3213 | 0 | } |
3214 | |
|
3215 | 0 | return ret; |
3216 | 0 | } |
3217 | | |
3218 | | LIBYANG_API_DEF LY_ERR |
3219 | | lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *target, struct lyd_node **match) |
3220 | 0 | { |
3221 | 0 | struct lyd_node **match_p, *iter, *parent; |
3222 | 0 | ly_bool found; |
3223 | |
|
3224 | 0 | LY_CHECK_ARG_RET(NULL, target, LY_EINVAL); |
3225 | |
|
3226 | 0 | if (!siblings) { |
3227 | | /* no data */ |
3228 | 0 | if (match) { |
3229 | 0 | *match = NULL; |
3230 | 0 | } |
3231 | 0 | return LY_ENOTFOUND; |
3232 | 0 | } |
3233 | | |
3234 | 0 | if ((siblings->schema && target->schema && |
3235 | 0 | !lyd_compare_schema_equal(lysc_data_parent(siblings->schema), lysc_data_parent(target->schema), 1))) { |
3236 | | /* schema mismatch */ |
3237 | 0 | if (match) { |
3238 | 0 | *match = NULL; |
3239 | 0 | } |
3240 | 0 | return LY_ENOTFOUND; |
3241 | 0 | } |
3242 | | |
3243 | | /* get first sibling */ |
3244 | 0 | siblings = lyd_first_sibling(siblings); |
3245 | |
|
3246 | 0 | parent = siblings->parent; |
3247 | 0 | if (target->schema && parent && parent->schema && ((struct lyd_node_inner *)parent)->children_ht) { |
3248 | 0 | assert(target->hash); |
3249 | | |
3250 | 0 | if (lysc_is_dup_inst_list(target->schema)) { |
3251 | | /* we must search the instances from beginning to find the first matching one */ |
3252 | 0 | found = 0; |
3253 | 0 | for (lyd_find_sibling_val(siblings, target->schema, NULL, 0, &iter); |
3254 | 0 | iter && lyd_compare_schema_equal(iter->schema, target->schema, 0); |
3255 | 0 | iter = iter->next) { |
3256 | 0 | if (!lyd_compare_single(target, iter, LYD_COMPARE_FULL_RECURSION)) { |
3257 | 0 | found = 1; |
3258 | 0 | break; |
3259 | 0 | } |
3260 | 0 | } |
3261 | 0 | if (found) { |
3262 | 0 | siblings = iter; |
3263 | 0 | } else { |
3264 | 0 | siblings = NULL; |
3265 | 0 | } |
3266 | 0 | } else { |
3267 | | /* find by hash */ |
3268 | 0 | if (!lyht_find(((struct lyd_node_inner *)parent)->children_ht, &target, target->hash, (void **)&match_p)) { |
3269 | 0 | siblings = *match_p; |
3270 | 0 | } else { |
3271 | | /* not found */ |
3272 | 0 | siblings = NULL; |
3273 | 0 | } |
3274 | 0 | } |
3275 | 0 | } else { |
3276 | | /* no children hash table or cannot be used */ |
3277 | 0 | for ( ; siblings; siblings = siblings->next) { |
3278 | 0 | if (lysc_is_dup_inst_list(target->schema)) { |
3279 | 0 | if (!lyd_compare_single(siblings, target, LYD_COMPARE_FULL_RECURSION)) { |
3280 | 0 | break; |
3281 | 0 | } |
3282 | 0 | } else { |
3283 | 0 | if (!lyd_compare_single(siblings, target, 0)) { |
3284 | 0 | break; |
3285 | 0 | } |
3286 | 0 | } |
3287 | 0 | } |
3288 | 0 | } |
3289 | | |
3290 | 0 | if (!siblings) { |
3291 | 0 | if (match) { |
3292 | 0 | *match = NULL; |
3293 | 0 | } |
3294 | 0 | return LY_ENOTFOUND; |
3295 | 0 | } |
3296 | | |
3297 | 0 | if (match) { |
3298 | 0 | *match = (struct lyd_node *)siblings; |
3299 | 0 | } |
3300 | 0 | return LY_SUCCESS; |
3301 | 0 | } |
3302 | | |
3303 | | LIBYANG_API_DEF LY_ERR |
3304 | | lyd_find_sibling_val(const struct lyd_node *siblings, const struct lysc_node *schema, const char *key_or_value, |
3305 | | uint32_t val_len, struct lyd_node **match) |
3306 | 0 | { |
3307 | 0 | LY_ERR rc; |
3308 | 0 | struct lyd_node *target = NULL; |
3309 | |
|
3310 | 0 | LY_CHECK_ARG_RET(NULL, schema, !(schema->nodetype & (LYS_CHOICE | LYS_CASE)), LY_EINVAL); |
3311 | 0 | if (!siblings) { |
3312 | | /* no data */ |
3313 | 0 | if (match) { |
3314 | 0 | *match = NULL; |
3315 | 0 | } |
3316 | 0 | return LY_ENOTFOUND; |
3317 | 0 | } |
3318 | | |
3319 | | /* no schema check, never reliable when ext data are involved */ |
3320 | | |
3321 | 0 | if (key_or_value && !val_len) { |
3322 | 0 | val_len = strlen(key_or_value); |
3323 | 0 | } |
3324 | |
|
3325 | 0 | if ((schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && key_or_value) { |
3326 | | /* create a data node and find the instance */ |
3327 | 0 | if (schema->nodetype == LYS_LEAFLIST) { |
3328 | | /* target used attributes: schema, hash, value */ |
3329 | 0 | rc = lyd_create_term(schema, NULL, key_or_value, val_len * 8, 0, 1, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, |
3330 | 0 | NULL, &target); |
3331 | 0 | LY_CHECK_RET(rc); |
3332 | 0 | } else { |
3333 | | /* target used attributes: schema, hash, child (all keys) */ |
3334 | 0 | LY_CHECK_RET(lyd_create_list2(schema, key_or_value, val_len, 1, &target)); |
3335 | 0 | } |
3336 | | |
3337 | | /* find it */ |
3338 | 0 | rc = lyd_find_sibling_first(siblings, target, match); |
3339 | 0 | } else { |
3340 | | /* find the first schema node instance */ |
3341 | 0 | rc = lyd_find_sibling_schema(siblings, schema, match); |
3342 | 0 | } |
3343 | | |
3344 | 0 | lyd_free_tree(target); |
3345 | 0 | return rc; |
3346 | 0 | } |
3347 | | |
3348 | | LIBYANG_API_DEF LY_ERR |
3349 | | lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_node *target, struct ly_set **set) |
3350 | 0 | { |
3351 | 0 | struct lyd_node **match_p, *first, *iter, *parent; |
3352 | 0 | uint32_t comp_opts; |
3353 | |
|
3354 | 0 | LY_CHECK_ARG_RET(NULL, target, set, LY_EINVAL); |
3355 | 0 | LY_CHECK_CTX_EQUAL_RET(__func__, siblings ? LYD_CTX(siblings) : NULL, LYD_CTX(target), LY_EINVAL); |
3356 | |
|
3357 | 0 | LY_CHECK_RET(ly_set_new(set)); |
3358 | |
|
3359 | 0 | if (!siblings || (siblings->schema && target->schema && |
3360 | 0 | (lysc_data_parent(siblings->schema) != lysc_data_parent(target->schema)))) { |
3361 | | /* no data or schema mismatch */ |
3362 | 0 | return LY_ENOTFOUND; |
3363 | 0 | } |
3364 | | |
3365 | | /* set options */ |
3366 | 0 | comp_opts = (lysc_is_dup_inst_list(target->schema) ? LYD_COMPARE_FULL_RECURSION : 0); |
3367 | | |
3368 | | /* get first sibling */ |
3369 | 0 | siblings = lyd_first_sibling(siblings); |
3370 | |
|
3371 | 0 | parent = siblings->parent; |
3372 | 0 | if (parent && parent->schema && ((struct lyd_node_inner *)parent)->children_ht) { |
3373 | 0 | assert(target->hash); |
3374 | | |
3375 | | /* find the first instance */ |
3376 | 0 | lyd_find_sibling_first(siblings, target, &first); |
3377 | 0 | if (first) { |
3378 | | /* add it so that it is the first in the set */ |
3379 | 0 | if (ly_set_add(*set, first, 1, NULL)) { |
3380 | 0 | goto error; |
3381 | 0 | } |
3382 | | |
3383 | | /* find by hash */ |
3384 | 0 | if (!lyht_find(((struct lyd_node_inner *)parent)->children_ht, &target, target->hash, (void **)&match_p)) { |
3385 | 0 | iter = *match_p; |
3386 | 0 | } else { |
3387 | | /* not found */ |
3388 | 0 | iter = NULL; |
3389 | 0 | } |
3390 | 0 | while (iter) { |
3391 | | /* add all found nodes into the set */ |
3392 | 0 | if ((iter != first) && !lyd_compare_single(iter, target, comp_opts) && ly_set_add(*set, iter, 1, NULL)) { |
3393 | 0 | goto error; |
3394 | 0 | } |
3395 | | |
3396 | | /* find next instance */ |
3397 | 0 | if (lyht_find_next(((struct lyd_node_inner *)parent)->children_ht, &iter, iter->hash, (void **)&match_p)) { |
3398 | 0 | iter = NULL; |
3399 | 0 | } else { |
3400 | 0 | iter = *match_p; |
3401 | 0 | } |
3402 | 0 | } |
3403 | 0 | } |
3404 | 0 | } else { |
3405 | | /* no children hash table */ |
3406 | 0 | LY_LIST_FOR(siblings, siblings) { |
3407 | 0 | if (!lyd_compare_single(target, siblings, comp_opts)) { |
3408 | 0 | ly_set_add(*set, (void *)siblings, 1, NULL); |
3409 | 0 | } |
3410 | 0 | } |
3411 | 0 | } |
3412 | | |
3413 | 0 | if (!(*set)->count) { |
3414 | 0 | return LY_ENOTFOUND; |
3415 | 0 | } |
3416 | 0 | return LY_SUCCESS; |
3417 | | |
3418 | 0 | error: |
3419 | 0 | ly_set_free(*set, NULL); |
3420 | 0 | *set = NULL; |
3421 | 0 | return LY_EMEM; |
3422 | 0 | } |
3423 | | |
3424 | | LIBYANG_API_DEF LY_ERR |
3425 | | lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struct lyd_node **match) |
3426 | 0 | { |
3427 | 0 | LY_CHECK_ARG_RET(NULL, name, LY_EINVAL); |
3428 | |
|
3429 | 0 | if (first && first->schema) { |
3430 | | /* find the actual first node */ |
3431 | 0 | while (first->prev->next) { |
3432 | 0 | first = first->prev; |
3433 | 0 | } |
3434 | |
|
3435 | 0 | first = first->prev; |
3436 | 0 | if (first->schema) { |
3437 | | /* no opaque nodes */ |
3438 | 0 | first = NULL; |
3439 | 0 | } else { |
3440 | | /* opaque nodes are at the end, find quickly the first */ |
3441 | 0 | while (!first->prev->schema) { |
3442 | 0 | first = first->prev; |
3443 | 0 | } |
3444 | 0 | } |
3445 | 0 | } |
3446 | |
|
3447 | 0 | for ( ; first; first = first->next) { |
3448 | 0 | assert(!first->schema); |
3449 | 0 | if (!strcmp(LYD_NAME(first), name)) { |
3450 | 0 | break; |
3451 | 0 | } |
3452 | 0 | } |
3453 | | |
3454 | 0 | if (match) { |
3455 | 0 | *match = (struct lyd_node *)first; |
3456 | 0 | } |
3457 | 0 | return first ? LY_SUCCESS : LY_ENOTFOUND; |
3458 | 0 | } |
3459 | | |
3460 | | LIBYANG_API_DEF LY_ERR |
3461 | | lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set) |
3462 | 0 | { |
3463 | 0 | LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL); |
3464 | |
|
3465 | 0 | return lyd_find_xpath3(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set); |
3466 | 0 | } |
3467 | | |
3468 | | LIBYANG_API_DEF LY_ERR |
3469 | | lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, struct ly_set **set) |
3470 | 0 | { |
3471 | 0 | LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL); |
3472 | |
|
3473 | 0 | return lyd_find_xpath3(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set); |
3474 | 0 | } |
3475 | | |
3476 | | LIBYANG_API_DEF LY_ERR |
3477 | | lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, LY_VALUE_FORMAT format, |
3478 | | void *prefix_data, const struct lyxp_var *vars, struct ly_set **set) |
3479 | 0 | { |
3480 | 0 | LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL); |
3481 | |
|
3482 | 0 | *set = NULL; |
3483 | |
|
3484 | 0 | return lyd_eval_xpath4(ctx_node, tree, NULL, xpath, format, prefix_data, vars, NULL, set, NULL, NULL, NULL); |
3485 | 0 | } |
3486 | | |
3487 | | LIBYANG_API_DEF LY_ERR |
3488 | | lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result) |
3489 | 0 | { |
3490 | 0 | return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result); |
3491 | 0 | } |
3492 | | |
3493 | | LIBYANG_API_DEF LY_ERR |
3494 | | lyd_eval_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, ly_bool *result) |
3495 | 0 | { |
3496 | 0 | return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, vars, result); |
3497 | 0 | } |
3498 | | |
3499 | | LIBYANG_API_DEF LY_ERR |
3500 | | lyd_eval_xpath3(const struct lyd_node *ctx_node, const struct lys_module *cur_mod, const char *xpath, |
3501 | | LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result) |
3502 | 0 | { |
3503 | 0 | return lyd_eval_xpath4(ctx_node, ctx_node, cur_mod, xpath, format, prefix_data, vars, NULL, NULL, NULL, NULL, result); |
3504 | 0 | } |
3505 | | |
3506 | | LIBYANG_API_DEF LY_ERR |
3507 | | lyd_eval_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const struct lys_module *cur_mod, |
3508 | | const char *xpath, LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, LY_XPATH_TYPE *ret_type, |
3509 | | struct ly_set **node_set, char **string, long double *number, ly_bool *boolean) |
3510 | 0 | { |
3511 | 0 | LY_ERR ret = LY_SUCCESS; |
3512 | 0 | struct lyxp_set xp_set = {0}; |
3513 | 0 | struct lyxp_expr *exp = NULL; |
3514 | 0 | uint32_t i; |
3515 | |
|
3516 | 0 | LY_CHECK_ARG_RET(NULL, tree, xpath, ((ret_type && node_set && string && number && boolean) || |
3517 | 0 | (node_set && !string && !number && !boolean) || (!node_set && string && !number && !boolean) || |
3518 | 0 | (!node_set && !string && number && !boolean) || (!node_set && !string && !number && boolean)), LY_EINVAL); |
3519 | | |
3520 | | /* parse expression */ |
3521 | 0 | ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(tree), ctx_node, xpath, 0, 1, &exp); |
3522 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3523 | | |
3524 | | /* evaluate expression */ |
3525 | 0 | ret = lyxp_eval(LYD_CTX(tree), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set, |
3526 | 0 | LYXP_IGNORE_WHEN); |
3527 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3528 | | |
3529 | | /* return expected result type without or with casting */ |
3530 | 0 | if (node_set) { |
3531 | | /* node set */ |
3532 | 0 | if (xp_set.type == LYXP_SET_NODE_SET) { |
3533 | | /* transform into a set */ |
3534 | 0 | LY_CHECK_GOTO(ret = ly_set_new(node_set), cleanup); |
3535 | 0 | (*node_set)->objs = malloc(xp_set.used * sizeof *(*node_set)->objs); |
3536 | 0 | LY_CHECK_ERR_GOTO(!(*node_set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup); |
3537 | 0 | (*node_set)->size = xp_set.used; |
3538 | 0 | for (i = 0; i < xp_set.used; ++i) { |
3539 | 0 | if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) { |
3540 | 0 | ret = ly_set_add(*node_set, xp_set.val.nodes[i].node, 1, NULL); |
3541 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3542 | 0 | } |
3543 | 0 | } |
3544 | 0 | if (ret_type) { |
3545 | 0 | *ret_type = LY_XPATH_NODE_SET; |
3546 | 0 | } |
3547 | 0 | } else if (!string && !number && !boolean) { |
3548 | 0 | LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath); |
3549 | 0 | ret = LY_EINVAL; |
3550 | 0 | goto cleanup; |
3551 | 0 | } |
3552 | 0 | } |
3553 | | |
3554 | 0 | if (string) { |
3555 | 0 | if ((xp_set.type != LYXP_SET_STRING) && !node_set) { |
3556 | | /* cast into string */ |
3557 | 0 | LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_STRING), cleanup); |
3558 | 0 | } |
3559 | 0 | if (xp_set.type == LYXP_SET_STRING) { |
3560 | | /* string */ |
3561 | 0 | *string = xp_set.val.str; |
3562 | 0 | xp_set.val.str = NULL; |
3563 | 0 | if (ret_type) { |
3564 | 0 | *ret_type = LY_XPATH_STRING; |
3565 | 0 | } |
3566 | 0 | } |
3567 | 0 | } |
3568 | | |
3569 | 0 | if (number) { |
3570 | 0 | if ((xp_set.type != LYXP_SET_NUMBER) && !node_set) { |
3571 | | /* cast into number */ |
3572 | 0 | LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_NUMBER), cleanup); |
3573 | 0 | } |
3574 | 0 | if (xp_set.type == LYXP_SET_NUMBER) { |
3575 | | /* number */ |
3576 | 0 | *number = xp_set.val.num; |
3577 | 0 | if (ret_type) { |
3578 | 0 | *ret_type = LY_XPATH_NUMBER; |
3579 | 0 | } |
3580 | 0 | } |
3581 | 0 | } |
3582 | | |
3583 | 0 | if (boolean) { |
3584 | 0 | if ((xp_set.type != LYXP_SET_BOOLEAN) && !node_set) { |
3585 | | /* cast into boolean */ |
3586 | 0 | LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN), cleanup); |
3587 | 0 | } |
3588 | 0 | if (xp_set.type == LYXP_SET_BOOLEAN) { |
3589 | | /* boolean */ |
3590 | 0 | *boolean = xp_set.val.bln; |
3591 | 0 | if (ret_type) { |
3592 | 0 | *ret_type = LY_XPATH_BOOLEAN; |
3593 | 0 | } |
3594 | 0 | } |
3595 | 0 | } |
3596 | | |
3597 | 0 | cleanup: |
3598 | 0 | lyxp_set_free_content(&xp_set); |
3599 | 0 | lyxp_expr_free(exp); |
3600 | 0 | return ret; |
3601 | 0 | } |
3602 | | |
3603 | | /** |
3604 | | * @brief Hash table node equal callback. |
3605 | | */ |
3606 | | static ly_bool |
3607 | | lyd_trim_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) |
3608 | 0 | { |
3609 | 0 | struct lyd_node *node1, *node2; |
3610 | |
|
3611 | 0 | node1 = *(struct lyd_node **)val1_p; |
3612 | 0 | node2 = *(struct lyd_node **)val2_p; |
3613 | |
|
3614 | 0 | return node1 == node2; |
3615 | 0 | } |
3616 | | |
3617 | | LIBYANG_API_DEF LY_ERR |
3618 | | lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars) |
3619 | 0 | { |
3620 | 0 | LY_ERR ret = LY_SUCCESS; |
3621 | 0 | struct ly_ctx *ctx = NULL; |
3622 | 0 | struct lyxp_set xp_set = {0}; |
3623 | 0 | struct lyxp_expr *exp = NULL; |
3624 | 0 | struct lyd_node *node, *parent; |
3625 | 0 | struct lyxp_set_hash_node hnode; |
3626 | 0 | struct ly_ht *parent_ht = NULL; |
3627 | 0 | struct ly_set free_set = {0}; |
3628 | 0 | uint32_t i, hash; |
3629 | 0 | ly_bool is_result; |
3630 | |
|
3631 | 0 | LY_CHECK_ARG_RET(NULL, tree, xpath, LY_EINVAL); |
3632 | |
|
3633 | 0 | if (!*tree) { |
3634 | | /* nothing to do */ |
3635 | 0 | goto cleanup; |
3636 | 0 | } |
3637 | | |
3638 | 0 | *tree = lyd_first_sibling(*tree); |
3639 | 0 | ctx = (struct ly_ctx *)LYD_CTX(*tree); |
3640 | | |
3641 | | /* parse expression */ |
3642 | 0 | ret = lyxp_expr_parse(ctx, NULL, xpath, 0, 1, &exp); |
3643 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3644 | | |
3645 | | /* evaluate expression */ |
3646 | 0 | ret = lyxp_eval(ctx, exp, NULL, LY_VALUE_JSON, NULL, *tree, *tree, *tree, vars, &xp_set, LYXP_IGNORE_WHEN); |
3647 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3648 | | |
3649 | | /* create hash table for all the parents of results */ |
3650 | 0 | parent_ht = lyht_new(32, sizeof node, lyd_trim_equal_cb, NULL, 1); |
3651 | 0 | LY_CHECK_GOTO(!parent_ht, cleanup); |
3652 | |
|
3653 | 0 | for (i = 0; i < xp_set.used; ++i) { |
3654 | 0 | if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) { |
3655 | | /* ignore */ |
3656 | 0 | continue; |
3657 | 0 | } |
3658 | | |
3659 | 0 | for (parent = xp_set.val.nodes[i].node->parent; parent; parent = parent->parent) { |
3660 | | /* add the parent into parent_ht */ |
3661 | 0 | ret = lyht_insert(parent_ht, &parent, parent->hash, NULL); |
3662 | 0 | if (ret == LY_EEXIST) { |
3663 | | /* shared parent, we are done */ |
3664 | 0 | break; |
3665 | 0 | } |
3666 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3667 | 0 | } |
3668 | 0 | } |
3669 | | |
3670 | 0 | hnode.type = LYXP_NODE_ELEM; |
3671 | 0 | LY_LIST_FOR(*tree, parent) { |
3672 | 0 | LYD_TREE_DFS_BEGIN(parent, node) { |
3673 | 0 | if (lysc_is_key(node->schema)) { |
3674 | | /* ignore */ |
3675 | 0 | goto next_iter; |
3676 | 0 | } |
3677 | | |
3678 | | /* check the results */ |
3679 | 0 | is_result = 0; |
3680 | 0 | if (xp_set.ht) { |
3681 | 0 | hnode.node = node; |
3682 | 0 | hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node); |
3683 | 0 | hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type); |
3684 | 0 | hash = lyht_hash_multi(hash, NULL, 0); |
3685 | |
|
3686 | 0 | if (!lyht_find(xp_set.ht, &hnode, hash, NULL)) { |
3687 | 0 | is_result = 1; |
3688 | 0 | } |
3689 | 0 | } else { |
3690 | | /* not enough elements for a hash table */ |
3691 | 0 | for (i = 0; i < xp_set.used; ++i) { |
3692 | 0 | if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) { |
3693 | | /* ignore */ |
3694 | 0 | continue; |
3695 | 0 | } |
3696 | | |
3697 | 0 | if (xp_set.val.nodes[i].node == node) { |
3698 | 0 | is_result = 1; |
3699 | 0 | break; |
3700 | 0 | } |
3701 | 0 | } |
3702 | 0 | } |
3703 | |
|
3704 | 0 | if (is_result) { |
3705 | | /* keep the whole subtree if the node is in the results */ |
3706 | 0 | LYD_TREE_DFS_continue = 1; |
3707 | 0 | } else if (lyht_find(parent_ht, &node, node->hash, NULL)) { |
3708 | | /* free the whole subtree if the node is not even among the selected parents */ |
3709 | 0 | ret = ly_set_add(&free_set, node, 1, NULL); |
3710 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3711 | 0 | LYD_TREE_DFS_continue = 1; |
3712 | 0 | } /* else keep the parent node because a subtree is in the results */ |
3713 | | |
3714 | 0 | next_iter: |
3715 | 0 | LYD_TREE_DFS_END(parent, node); |
3716 | 0 | } |
3717 | 0 | } |
3718 | | |
3719 | | /* free */ |
3720 | 0 | for (i = 0; i < free_set.count; ++i) { |
3721 | 0 | node = free_set.dnodes[i]; |
3722 | 0 | if (*tree == node) { |
3723 | 0 | *tree = (*tree)->next; |
3724 | 0 | } |
3725 | 0 | lyd_free_tree(node); |
3726 | 0 | } |
3727 | |
|
3728 | 0 | cleanup: |
3729 | 0 | lyxp_set_free_content(&xp_set); |
3730 | 0 | lyxp_expr_free(exp); |
3731 | 0 | lyht_free(parent_ht, NULL); |
3732 | 0 | ly_set_erase(&free_set, NULL); |
3733 | 0 | return ret; |
3734 | 0 | } |
3735 | | |
3736 | | LIBYANG_API_DEF LY_ERR |
3737 | | lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, struct lyd_node **match) |
3738 | 0 | { |
3739 | 0 | LY_ERR ret = LY_SUCCESS; |
3740 | 0 | struct lyxp_expr *expr = NULL; |
3741 | 0 | struct ly_path *lypath = NULL; |
3742 | 0 | const struct lyd_node *tree = NULL; |
3743 | |
|
3744 | 0 | LY_CHECK_ARG_RET(NULL, ctx_node, ctx_node->schema, path, LY_EINVAL); |
3745 | | |
3746 | | /* parse the path */ |
3747 | 0 | ret = ly_path_parse(LYD_CTX(ctx_node), ctx_node->schema, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, |
3748 | 0 | LY_PATH_PRED_SIMPLE, &expr); |
3749 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3750 | | |
3751 | | /* compile the path */ |
3752 | 0 | ret = ly_path_compile(LYD_CTX(ctx_node), ctx_node->schema, expr, |
3753 | 0 | output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, 0, LY_VALUE_JSON, NULL, &lypath); |
3754 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3755 | |
|
3756 | 0 | if (lypath[0].doc_root) { |
3757 | | /* use the root context node for absolute paths, avoids specific XPath evaluation rules of extensions */ |
3758 | 0 | for (tree = ctx_node; tree->parent; tree = tree->parent) {} |
3759 | 0 | ctx_node = NULL; |
3760 | 0 | } |
3761 | | |
3762 | | /* evaluate the path */ |
3763 | 0 | ret = ly_path_eval_partial(lypath, ctx_node, tree, NULL, 0, NULL, match); |
3764 | |
|
3765 | 0 | cleanup: |
3766 | 0 | lyxp_expr_free(expr); |
3767 | 0 | ly_path_free(lypath); |
3768 | 0 | return ret; |
3769 | 0 | } |
3770 | | |
3771 | | LIBYANG_API_DEF LY_ERR |
3772 | | lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct lyd_node **match) |
3773 | 0 | { |
3774 | 0 | LY_ERR ret; |
3775 | 0 | struct lyd_node *m; |
3776 | |
|
3777 | 0 | LY_CHECK_ARG_RET(NULL, path, LY_EINVAL); |
3778 | |
|
3779 | 0 | ret = ly_path_eval(path, tree, NULL, &m); |
3780 | 0 | if (ret) { |
3781 | 0 | if (match) { |
3782 | 0 | *match = NULL; |
3783 | 0 | } |
3784 | 0 | return LY_ENOTFOUND; |
3785 | 0 | } |
3786 | | |
3787 | 0 | if (match) { |
3788 | 0 | *match = m; |
3789 | 0 | } |
3790 | 0 | return LY_SUCCESS; |
3791 | 0 | } |
3792 | | |
3793 | | LY_ERR |
3794 | | lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct lyd_leafref_links_rec **record, ly_bool create) |
3795 | 0 | { |
3796 | 0 | struct ly_ctx_shared_data *ctx_data; |
3797 | 0 | LY_ERR ret = LY_SUCCESS; |
3798 | 0 | uint32_t hash; |
3799 | 0 | struct lyd_leafref_links_rec rec = {0}; |
3800 | 0 | struct lyd_leafref_links_rec *rec_p = &rec; |
3801 | 0 | struct lyd_leafref_links_rec **rec_p2; |
3802 | |
|
3803 | 0 | assert(node); |
3804 | 0 | assert(record); |
3805 | | |
3806 | 0 | *record = NULL; |
3807 | |
|
3808 | 0 | if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) { |
3809 | 0 | return LY_EDENIED; |
3810 | 0 | } |
3811 | | |
3812 | 0 | ctx_data = ly_ctx_shared_data_get(LYD_CTX(node)); |
3813 | 0 | rec.node = node; |
3814 | 0 | hash = lyht_hash((const char *)&node, sizeof node); |
3815 | | |
3816 | | /* LL LOCK */ |
3817 | 0 | pthread_mutex_lock(&ctx_data->leafref_links_lock); |
3818 | |
|
3819 | 0 | ret = lyht_find(ctx_data->leafref_links_ht, &rec_p, hash, (void **)&rec_p2); |
3820 | 0 | if ((ret == LY_ENOTFOUND) && create) { |
3821 | | /* create a new record */ |
3822 | 0 | rec_p = calloc(1, sizeof rec); |
3823 | 0 | rec_p->node = node; |
3824 | 0 | LY_CHECK_ERR_GOTO(!rec_p, LOGMEM(LYD_CTX(node)), cleanup); |
3825 | 0 | ret = lyht_insert_no_check(ctx_data->leafref_links_ht, &rec_p, hash, (void **)&rec_p2); |
3826 | 0 | LY_CHECK_ERR_GOTO(ret, free(rec_p), cleanup); |
3827 | 0 | } |
3828 | | |
3829 | 0 | cleanup: |
3830 | 0 | if (!ret) { |
3831 | 0 | *record = *rec_p2; |
3832 | 0 | } |
3833 | | |
3834 | | /* LL UNLOCK */ |
3835 | 0 | pthread_mutex_unlock(&ctx_data->leafref_links_lock); |
3836 | |
|
3837 | 0 | return ret; |
3838 | 0 | } |
3839 | | |
3840 | | LIBYANG_API_DEF LY_ERR |
3841 | | lyd_leafref_get_links(const struct lyd_node_term *node, const struct lyd_leafref_links_rec **record) |
3842 | 0 | { |
3843 | 0 | LY_CHECK_ARG_RET(NULL, node, record, LY_EINVAL); |
3844 | |
|
3845 | 0 | return lyd_get_or_create_leafref_links_record(node, (struct lyd_leafref_links_rec **)record, 0); |
3846 | 0 | } |
3847 | | |
3848 | | LY_ERR |
3849 | | lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node) |
3850 | 0 | { |
3851 | 0 | const struct lyd_node_term **item = NULL; |
3852 | 0 | struct lyd_leafref_links_rec *rec; |
3853 | 0 | LY_ARRAY_COUNT_TYPE u; |
3854 | |
|
3855 | 0 | assert(node); |
3856 | 0 | assert(leafref_node); |
3857 | | |
3858 | 0 | if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) { |
3859 | 0 | return LY_EDENIED; |
3860 | 0 | } |
3861 | | |
3862 | | /* add leafref node into the list of target node */ |
3863 | 0 | LY_CHECK_RET(lyd_get_or_create_leafref_links_record(node, &rec, 1)); |
3864 | 0 | LY_ARRAY_FOR(rec->leafref_nodes, u) { |
3865 | 0 | if (rec->leafref_nodes[u] == leafref_node) { |
3866 | 0 | return LY_SUCCESS; |
3867 | 0 | } |
3868 | 0 | } |
3869 | | |
3870 | 0 | LY_ARRAY_NEW_RET(LYD_CTX(node), rec->leafref_nodes, item, LY_EMEM); |
3871 | 0 | *item = leafref_node; |
3872 | | |
3873 | | /* add target node into the list of leafref node*/ |
3874 | 0 | LY_CHECK_RET(lyd_get_or_create_leafref_links_record(leafref_node, &rec, 1)); |
3875 | 0 | LY_ARRAY_FOR(rec->target_nodes, u) { |
3876 | 0 | if (rec->target_nodes[u] == node) { |
3877 | 0 | return LY_SUCCESS; |
3878 | 0 | } |
3879 | 0 | } |
3880 | | |
3881 | 0 | LY_ARRAY_NEW_RET(LYD_CTX(node), rec->target_nodes, item, LY_EMEM); |
3882 | 0 | *item = node; |
3883 | |
|
3884 | 0 | return LY_SUCCESS; |
3885 | 0 | } |
3886 | | |
3887 | | /** |
3888 | | * @brief Traverse through data tree node suitable types and adds leafrefs links to the given nodes |
3889 | | * |
3890 | | * This API requires usage of ::LY_CTX_LEAFREF_LINKING context flag. |
3891 | | * |
3892 | | * @param[in] tree The data tree root node. |
3893 | | * @param[in] cur_node The current data node. |
3894 | | * @param[in] value The current node value. |
3895 | | * @param[in] type The leaf/leaf-list type of given data node. |
3896 | | * |
3897 | | * @return LY_SUCCESS on success. |
3898 | | * @return LY_ERR value on error. |
3899 | | */ |
3900 | | static LY_ERR |
3901 | | lyd_leafref_link_node_tree_type(const struct lyd_node *tree, const struct lyd_node *cur_node, struct lyd_value *value, const struct lysc_type *type) |
3902 | 0 | { |
3903 | 0 | char *errmsg; |
3904 | 0 | struct ly_set *targets = NULL; |
3905 | 0 | LY_ERR ret = LY_SUCCESS; |
3906 | 0 | struct lysc_type_leafref *lref; |
3907 | 0 | struct lysc_type_union *un; |
3908 | 0 | LY_ARRAY_COUNT_TYPE u; |
3909 | 0 | uint32_t i; |
3910 | 0 | struct lyd_node_term *leafref_node = (struct lyd_node_term *)cur_node; |
3911 | |
|
3912 | 0 | if (type->basetype == LY_TYPE_LEAFREF) { |
3913 | 0 | lref = (struct lysc_type_leafref *)type; |
3914 | 0 | ly_set_free(targets, NULL); |
3915 | 0 | if (lyplg_type_resolve_leafref(lref, cur_node, value, tree, &targets, &errmsg)) { |
3916 | | /* leafref target not found */ |
3917 | 0 | free(errmsg); |
3918 | 0 | } else { |
3919 | | /* leafref target found, link it */ |
3920 | 0 | for (i = 0; i < targets->count; ++i) { |
3921 | 0 | if (targets->dnodes[i]->schema->nodetype & LYD_NODE_TERM) { |
3922 | 0 | ret = lyd_link_leafref_node((struct lyd_node_term *)targets->dnodes[i], leafref_node); |
3923 | 0 | LY_CHECK_GOTO(ret, cleanup); |
3924 | 0 | } |
3925 | 0 | } |
3926 | 0 | } |
3927 | 0 | } else if (type->basetype == LY_TYPE_UNION) { |
3928 | 0 | un = (struct lysc_type_union *)type; |
3929 | 0 | LY_ARRAY_FOR(un->types, u) { |
3930 | 0 | ret = lyd_leafref_link_node_tree_type(tree, cur_node, &leafref_node->value.subvalue->value, un->types[u]); |
3931 | 0 | LY_CHECK_GOTO(ret, cleanup) |
3932 | 0 | } |
3933 | 0 | } |
3934 | | |
3935 | 0 | cleanup: |
3936 | 0 | ly_set_free(targets, NULL); |
3937 | 0 | return ret; |
3938 | 0 | } |
3939 | | |
3940 | | LIBYANG_API_DEF LY_ERR |
3941 | | lyd_leafref_link_node_tree(const struct lyd_node *tree) |
3942 | 0 | { |
3943 | 0 | const struct lyd_node *sibling, *elem; |
3944 | 0 | struct lyd_node_term *cur_node; |
3945 | 0 | struct lysc_node_leaf *leaf_schema; |
3946 | |
|
3947 | 0 | LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL); |
3948 | |
|
3949 | 0 | if (!(ly_ctx_get_options(LYD_CTX(tree)) & LY_CTX_LEAFREF_LINKING)) { |
3950 | 0 | return LY_EDENIED; |
3951 | 0 | } |
3952 | | |
3953 | 0 | LY_LIST_FOR(tree, sibling) { |
3954 | 0 | LYD_TREE_DFS_BEGIN(sibling, elem) { |
3955 | 0 | if (elem->schema && (elem->schema->nodetype & LYD_NODE_TERM)) { |
3956 | 0 | leaf_schema = (struct lysc_node_leaf *)elem->schema; |
3957 | 0 | cur_node = (struct lyd_node_term *)elem; |
3958 | 0 | LY_CHECK_RET(lyd_leafref_link_node_tree_type(tree, elem, &cur_node->value, leaf_schema->type)); |
3959 | 0 | } |
3960 | 0 | LYD_TREE_DFS_END(sibling, elem); |
3961 | 0 | } |
3962 | 0 | } |
3963 | | |
3964 | 0 | return LY_SUCCESS; |
3965 | 0 | } |
3966 | | |
3967 | | LY_ERR |
3968 | | lyd_unlink_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node) |
3969 | 0 | { |
3970 | 0 | LY_ERR ret; |
3971 | 0 | struct lyd_leafref_links_rec *rec; |
3972 | |
|
3973 | 0 | assert(node); |
3974 | 0 | assert(leafref_node); |
3975 | | |
3976 | 0 | if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) { |
3977 | 0 | return LY_EDENIED; |
3978 | 0 | } |
3979 | | |
3980 | | /* remove link from target node to leafref node */ |
3981 | 0 | ret = lyd_get_or_create_leafref_links_record(node, &rec, 0); |
3982 | 0 | if (ret == LY_SUCCESS) { |
3983 | 0 | LY_ARRAY_REMOVE_VALUE(rec->leafref_nodes, leafref_node); |
3984 | 0 | if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (LY_ARRAY_COUNT(rec->target_nodes) == 0)) { |
3985 | 0 | lyd_free_leafref_nodes(node); |
3986 | 0 | } |
3987 | 0 | } else if (ret != LY_ENOTFOUND) { |
3988 | 0 | return ret; |
3989 | 0 | } |
3990 | | |
3991 | | /* remove link from leafref node to target node */ |
3992 | 0 | ret = lyd_get_or_create_leafref_links_record(leafref_node, &rec, 0); |
3993 | 0 | if (ret == LY_SUCCESS) { |
3994 | 0 | LY_ARRAY_REMOVE_VALUE(rec->target_nodes, node); |
3995 | 0 | if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (LY_ARRAY_COUNT(rec->target_nodes) == 0)) { |
3996 | 0 | lyd_free_leafref_nodes(leafref_node); |
3997 | 0 | } |
3998 | 0 | } else if (ret != LY_ENOTFOUND) { |
3999 | 0 | return ret; |
4000 | 0 | } |
4001 | | |
4002 | 0 | return LY_SUCCESS; |
4003 | 0 | } |