blob: 42b6d10761e0a227b2e422dd864b079e3bcb942e [file] [log] [blame]
Michal Vaskod59035b2020-07-08 12:00:06 +02001/**
2 * @file diff.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief diff functions
5 *
6 * Copyright (c) 2020 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14#define _XOPEN_SOURCE 500
Radek Krejci33185cb2020-07-11 23:15:22 +020015#define _POSIX_C_SOURCE 200809L
Michal Vaskod59035b2020-07-08 12:00:06 +020016
17#include "diff.h"
18
19#include <assert.h>
20#include <stddef.h>
21#include <string.h>
22
23#include "common.h"
24#include "log.h"
25#include "tree_data_internal.h"
26#include "tree_schema.h"
27#include "tree_schema_internal.h"
28
29static const char *
30lyd_diff_op2str(enum lyd_diff_op op)
31{
32 switch (op) {
33 case LYD_DIFF_OP_CREATE:
34 return "create";
35 case LYD_DIFF_OP_DELETE:
36 return "delete";
37 case LYD_DIFF_OP_REPLACE:
38 return "replace";
39 case LYD_DIFF_OP_NONE:
40 return "none";
41 }
42
43 LOGINT(NULL);
44 return NULL;
45}
46
Michal Vaskoe6323f62020-07-09 15:49:02 +020047static enum lyd_diff_op
48lyd_diff_str2op(const char *str)
49{
50 switch (str[0]) {
51 case 'c':
52 assert(!strcmp(str, "create"));
53 return LYD_DIFF_OP_CREATE;
54 case 'd':
55 assert(!strcmp(str, "delete"));
56 return LYD_DIFF_OP_DELETE;
57 case 'r':
58 assert(!strcmp(str, "replace"));
59 return LYD_DIFF_OP_REPLACE;
60 case 'n':
61 assert(!strcmp(str, "none"));
62 return LYD_DIFF_OP_NONE;
63 }
64
65 LOGINT(NULL);
66 return 0;
67}
68
Michal Vaskod59035b2020-07-08 12:00:06 +020069LY_ERR
70lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
71 const char *key, const char *value, const char *orig_key, struct lyd_node **diff)
72{
73 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
74 const struct lyd_node *parent = NULL;
75 const struct lys_module *yang_mod;
76
77 assert(diff);
78
79 /* find the first existing parent */
80 siblings = *diff;
81 while (1) {
82 /* find next node parent */
83 parent = node;
84 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
85 parent = (struct lyd_node *)parent->parent;
86 }
87 if (parent == node) {
88 /* no more parents to find */
89 break;
90 }
91
92 /* check whether it exists in the diff */
93 if (lyd_find_sibling_first(siblings, parent, &match)) {
94 break;
95 }
96
97 /* another parent found */
98 diff_parent = match;
99
100 /* move down in the diff */
101 siblings = LYD_CHILD(match);
102 }
103
104 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200105 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
106 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200107
108 /* find the first duplicated parent */
109 if (!diff_parent) {
110 diff_parent = (struct lyd_node *)dup->parent;
111 while (diff_parent && diff_parent->parent) {
112 diff_parent = (struct lyd_node *)diff_parent->parent;
113 }
114 } else {
115 diff_parent = (struct lyd_node *)dup;
116 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
117 diff_parent = (struct lyd_node *)diff_parent->parent;
118 }
119 }
120
121 /* no parent existed, must be manually connected */
122 if (!diff_parent) {
123 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200124 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200125 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200126 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200127 }
128
129 /* get module with the operation metadata */
130 yang_mod = LYD_NODE_CTX(node)->list.objs[1];
131 assert(!strcmp(yang_mod->name, "yang"));
132
133 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200134 if (diff_parent && (diff_parent != dup)) {
135 LY_CHECK_RET(lyd_new_meta(diff_parent, yang_mod, "operation", "none", NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200136 }
137
138 /* add subtree operation */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200139 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "operation", lyd_diff_op2str(op), NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200140
141 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200142 if (orig_default) {
143 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-default", orig_default, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200144 }
145
146 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200147 if (orig_value) {
148 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-value", orig_value, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200149 }
150
151 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200152 if (key) {
153 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "key", key, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200154 }
155
156 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200157 if (value) {
158 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "value", value, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200159 }
160
161 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200162 if (orig_key) {
163 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-key", orig_key, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200164 }
165
166 return LY_SUCCESS;
167}
168
169/**
170 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
171 *
172 * @param[in] first
173 * @param[in] schema Schema node of the list/leaf-list.
174 * @param[in,out] userord Sized array of userord items.
175 * @return Userord item for all the user-ordered list/leaf-list instances.
176 */
177static struct lyd_diff_userord *
178lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
179{
180 struct lyd_diff_userord *item;
181 const struct lyd_node *iter, **node;
182 LY_ARRAY_COUNT_TYPE u;
183
184 LY_ARRAY_FOR(*userord, u) {
185 if ((*userord)[u].schema == schema) {
186 return &(*userord)[u];
187 }
188 }
189
190 /* it was not added yet, add it now */
191 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
192
193 item->schema = schema;
194 item->pos = 0;
195 item->inst = NULL;
196
197 /* store all the instance pointers in the current order */
198 if (first) {
199 if (first->parent) {
200 iter = first->parent->child;
201 } else {
202 for (iter = first; iter->prev->next; iter = iter->prev);
203 }
204 for (; iter; iter = iter->next) {
205 if (iter->schema == first->schema) {
206 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
207 *node = iter;
208 }
209 }
210 }
211
212 return item;
213}
214
215/**
216 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
217 * lists/leaf-lists.
218 *
219 * @param[in] first Node from the first tree, can be NULL (on create).
220 * @param[in] second Node from the second tree, can be NULL (on delete).
221 * @param[in] options Diff options.
222 * @param[in,out] userord Sized array of userord items for keeping the current node order.
223 * @param[out] op Operation.
224 * @param[out] orig_default Original default metadata.
225 * @param[out] value Value metadata.
226 * @param[out] orig_value Original value metadata
227 * @param[out] key Key metadata.
228 * @param[out] orig_key Original key metadata.
229 * @return LY_SUCCESS on success,
230 * @return LY_ENOT if there is no change to be added into diff,
231 * @return LY_ERR value on other errors.
232 */
233static LY_ERR
234lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, int options,
235 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
236 char **orig_value, char **key, char **orig_key)
237{
238 const struct lysc_node *schema;
239 int dynamic;
240 size_t buflen, bufused, first_pos, second_pos;
241 struct lyd_diff_userord *userord_item;
242
243 assert(first || second);
244
245 *orig_default = NULL;
246 *value = NULL;
247 *orig_value = NULL;
248 *key = NULL;
249 *orig_key = NULL;
250
251 schema = first ? first->schema : second->schema;
252 assert(lysc_is_userordered(schema));
253
254 /* get userord entry */
255 userord_item = lyd_diff_userord_get(first, schema, userord);
256 LY_CHECK_RET(!userord_item, LY_EMEM);
257
258 /* prepare position of the next instance */
259 second_pos = userord_item->pos++;
260
261 /* find user-ordered first position */
262 if (first) {
263 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
264 if (userord_item->inst[first_pos] == first) {
265 break;
266 }
267 }
268 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
269 } else {
270 first_pos = 0;
271 }
272
273 /* learn operation first */
274 if (!second) {
275 *op = LYD_DIFF_OP_DELETE;
276 } else if (!first) {
277 *op = LYD_DIFF_OP_CREATE;
278 } else {
279 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
Michal Vasko8f359bf2020-07-28 10:41:15 +0200280 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200281 /* in first, there is a different instance on the second position, we are going to move 'first' node */
282 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200283 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200284 /* default flag change */
285 *op = LYD_DIFF_OP_NONE;
286 } else {
287 /* no changes */
288 return LY_ENOT;
289 }
290 }
291
292 /*
293 * set each attribute correctly based on the operation and node type
294 */
295
296 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200297 if ((options & LYD_DIFF_DEFAULTS) && (schema->nodetype == LYS_LEAFLIST)
Michal Vaskoe6323f62020-07-09 15:49:02 +0200298 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))
299 && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200300 if (first->flags & LYD_DEFAULT) {
301 *orig_default = "true";
302 } else {
303 *orig_default = "false";
304 }
305 }
306
307 /* value */
308 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
309 if (second_pos) {
310 *value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[second_pos - 1], &dynamic);
311 if (!dynamic) {
312 *value = strdup(*value);
313 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
314 }
315 } else {
316 *value = strdup("");
317 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
318 }
319 }
320
321 /* orig-value */
322 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
323 if (first_pos) {
324 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[first_pos - 1], &dynamic);
325 if (!dynamic) {
326 *orig_value = strdup(*orig_value);
327 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
328 }
329 } else {
330 *orig_value = strdup("");
331 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
332 }
333 }
334
335 /* key */
336 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op ==LYD_DIFF_OP_CREATE))) {
337 if (second_pos) {
338 buflen = bufused = 0;
339 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
340 } else {
341 *key = strdup("");
342 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
343 }
344 }
345
346 /* orig-key */
347 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
348 if (first_pos) {
349 buflen = bufused = 0;
350 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
351 } else {
352 *orig_key = strdup("");
353 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
354 }
355 }
356
357 /*
358 * update our instances - apply the change
359 */
360 if (*op == LYD_DIFF_OP_CREATE) {
361 /* insert the instance */
362 LY_ARRAY_RESIZE_ERR_RET(schema->module->ctx, userord_item->inst, LY_ARRAY_COUNT(userord_item->inst) + 1,
363 ;, LY_EMEM);
364 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
365 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
366 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
367 }
368 LY_ARRAY_INCREMENT(userord_item->inst);
369 userord_item->inst[second_pos] = second;
370
371 } else if (*op == LYD_DIFF_OP_DELETE) {
372 /* remove the instance */
373 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
374 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
375 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
376 }
377 LY_ARRAY_DECREMENT(userord_item->inst);
378
379 } else if (*op == LYD_DIFF_OP_REPLACE) {
380 /* move the instances */
381 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
382 (first_pos - second_pos) * sizeof *userord_item->inst);
383 userord_item->inst[second_pos] = first;
384 }
385
386 return LY_SUCCESS;
387}
388
389/**
390 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
391 * lists/leaf-lists.
392 *
393 * @param[in] first Node from the first tree, can be NULL (on create).
394 * @param[in] second Node from the second tree, can be NULL (on delete).
395 * @param[in] options Diff options.
396 * @param[out] op Operation.
397 * @param[out] orig_default Original default metadata.
398 * @param[out] orig_value Original value metadata.
399 * @return LY_SUCCESS on success,
400 * @return LY_ENOT if there is no change to be added into diff,
401 * @return LY_ERR value on other errors.
402 */
403static LY_ERR
404lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, int options, enum lyd_diff_op *op,
405 const char **orig_default, char **orig_value)
406{
407 const struct lysc_node *schema;
408 int dynamic;
409
410 assert(first || second);
411
412 *orig_default = NULL;
413 *orig_value = NULL;
414
415 schema = first ? first->schema : second->schema;
416 assert(!lysc_is_userordered(schema));
417
418 /* learn operation first */
419 if (!second) {
420 *op = LYD_DIFF_OP_DELETE;
421 } else if (!first) {
422 *op = LYD_DIFF_OP_CREATE;
423 } else {
424 switch (schema->nodetype) {
425 case LYS_CONTAINER:
426 case LYS_RPC:
427 case LYS_ACTION:
428 case LYS_NOTIF:
429 /* no changes */
430 return LY_ENOT;
431 case LYS_LIST:
432 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200433 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200434 /* default flag change */
435 *op = LYD_DIFF_OP_NONE;
436 } else {
437 /* no changes */
438 return LY_ENOT;
439 }
440 break;
441 case LYS_LEAF:
442 case LYS_ANYXML:
443 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200444 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200445 /* different values */
446 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200447 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200448 /* default flag change */
449 *op = LYD_DIFF_OP_NONE;
450 } else {
451 /* no changes */
452 return LY_ENOT;
453 }
454 break;
455 default:
456 LOGINT_RET(schema->module->ctx);
457 }
458 }
459
460 /*
461 * set each attribute correctly based on the operation and node type
462 */
463
464 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200465 if ((options & LYD_DIFF_DEFAULTS) && (schema->nodetype & LYD_NODE_TERM)
Michal Vaskoe6323f62020-07-09 15:49:02 +0200466 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))
467 && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200468 if (first->flags & LYD_DEFAULT) {
469 *orig_default = "true";
470 } else {
471 *orig_default = "false";
472 }
473 }
474
475 /* orig-value */
476 if ((schema->nodetype == LYS_LEAF) && (*op == LYD_DIFF_OP_REPLACE)) {
477 /* leaf */
478 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)first, &dynamic);
479 if (!dynamic) {
480 *orig_value = strdup(*orig_value);
481 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
482 }
483 }
484
485 return LY_SUCCESS;
486}
487
488/**
489 * @brief Perform diff for all siblings at certain depth, recursively.
490 *
491 * For user-ordered lists/leaf-lists a specific structure is used for storing
492 * the current order. The idea is to apply all the generated diff changes
493 * virtually on the first tree so that we can continue to generate correct
494 * changes after some were already generated.
495 *
496 * The algorithm then uses second tree position-based changes with a before
497 * (preceding) item anchor.
498 *
499 * Example:
500 *
501 * Virtual first tree leaf-list order:
502 * 1 2 [3] 4 5
503 *
504 * Second tree leaf-list order:
505 * 1 2 [5] 3 4
506 *
507 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
508 * match - they do not - move nodes so that the 3rd position node is final ->
509 * -> move node 5 to the 3rd position -> move node 5 after node 2.
510 *
511 * Required properties:
512 * Stored operations (move) should not be affected by later operations -
513 * - would cause a redundantly long list of operations, possibly inifinite.
514 *
515 * Implemenation justification:
516 * First, all delete operations and only then move/create operations are stored.
517 * Also, preceding anchor is used and after each iteration another node is
518 * at its final position. That results in the invariant that all preceding
519 * nodes are final and will not be changed by the later operations, meaning
520 * they can safely be used as anchors for the later operations.
521 *
522 * @param[in] first First tree first sibling.
523 * @param[in] second Second tree first sibling.
524 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200525 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200526 * @param[in,out] diff Diff to append to.
527 * @return LY_ERR value.
528 */
529static LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +0200530lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, int options, int nosiblings,
531 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200532{
533 LY_ERR ret = LY_SUCCESS;
534 const struct lyd_node *iter_first, *iter_second;
535 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200536 struct lyd_diff_userord *userord = NULL;
537 LY_ARRAY_COUNT_TYPE u;
538 enum lyd_diff_op op;
539 const char *orig_default;
540 char *orig_value, *key, *value, *orig_key;
541
Michal Vaskod59035b2020-07-08 12:00:06 +0200542 /* compare first tree to the second tree - delete, replace, none */
543 LY_LIST_FOR(first, iter_first) {
544 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200545 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200546 /* skip default nodes */
547 continue;
548 }
549
550 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
551 /* try to find the exact instance */
552 lyd_find_sibling_first(second, iter_first, &match_second);
553 } else {
554 /* try to simply find the node, there cannot be more instances */
555 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
556 }
557
Michal Vasko3a41dff2020-07-15 14:30:28 +0200558 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200559 /* ignore default nodes */
560 match_second = NULL;
561 }
562
563 if (lysc_is_userordered(iter_first->schema)) {
564 if (match_second) {
565 /* we are handling only user-ordered node delete now */
566 continue;
567 }
568
569 /* get all the attributes */
570 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
571 &value, &orig_value, &key, &orig_key), cleanup);
572
573 /* there must be changes, it is deleted */
574 assert(op == LYD_DIFF_OP_DELETE);
575 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
576
577 free(orig_value);
578 free(key);
579 free(value);
580 free(orig_key);
581 LY_CHECK_GOTO(ret, cleanup);
582 } else {
583 /* get all the attributes */
584 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
585
586 /* add into diff if there are any changes */
587 if (!ret) {
588 if (op == LYD_DIFF_OP_DELETE) {
589 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
590 } else {
591 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
592 }
593
594 free(orig_value);
595 LY_CHECK_GOTO(ret, cleanup);
596 } else if (ret == LY_ENOT) {
597 ret = LY_SUCCESS;
598 } else {
599 goto cleanup;
600 }
601 }
602
603 /* check descendants, if any, recursively */
604 if (match_second) {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200605 LY_CHECK_GOTO(lyd_diff_siblings_r(LYD_CHILD(iter_first), LYD_CHILD(match_second), options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200606 }
607
608 if (nosiblings) {
609 break;
610 }
611 }
612
613 /* reset all cached positions */
614 LY_ARRAY_FOR(userord, u) {
615 userord[u].pos = 0;
616 }
617
618 /* compare second tree to the first tree - create, user-ordered move */
619 LY_LIST_FOR(second, iter_second) {
620 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200621 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200622 /* skip default nodes */
623 continue;
624 }
625
626 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
627 lyd_find_sibling_first(first, iter_second, &match_first);
628 } else {
629 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
630 }
631
Michal Vasko3a41dff2020-07-15 14:30:28 +0200632 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200633 /* ignore default nodes */
634 match_first = NULL;
635 }
636
637 if (lysc_is_userordered(iter_second->schema)) {
638 /* get all the attributes */
639 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
640 &value, &orig_value, &key, &orig_key);
641
642 /* add into diff if there are any changes */
643 if (!ret) {
644 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
645
646 free(orig_value);
647 free(key);
648 free(value);
649 free(orig_key);
650 LY_CHECK_GOTO(ret, cleanup);
651 } else if (ret == LY_ENOT) {
652 ret = LY_SUCCESS;
653 } else {
654 goto cleanup;
655 }
656 } else if (!match_first) {
657 /* get all the attributes */
658 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
659
660 /* there must be changes, it is created */
661 assert(op == LYD_DIFF_OP_CREATE);
662 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
663
664 free(orig_value);
665 LY_CHECK_GOTO(ret, cleanup);
666 } /* else was handled */
667
668 if (nosiblings) {
669 break;
670 }
671 }
672
673cleanup:
674 LY_ARRAY_FOR(userord, u) {
675 LY_ARRAY_FREE(userord[u].inst);
676 }
677 LY_ARRAY_FREE(userord);
678 return ret;
679}
680
Michal Vasko3a41dff2020-07-15 14:30:28 +0200681static LY_ERR
682lyd_diff(const struct lyd_node *first, const struct lyd_node *second, int options, int nosiblings, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200683{
684 const struct ly_ctx *ctx;
685
686 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
687
688 if (first) {
689 ctx = LYD_NODE_CTX(first);
690 } else if (second) {
691 ctx = LYD_NODE_CTX(second);
692 } else {
693 ctx = NULL;
694 }
695
696 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
697 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
698 return LY_EINVAL;
699 }
700
701 *diff = NULL;
702
Michal Vasko3a41dff2020-07-15 14:30:28 +0200703 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
704}
705
706API LY_ERR
707lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
708{
709 return lyd_diff(first, second, options, 1, diff);
710}
711
712API LY_ERR
713lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
714{
715 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200716}
717
718/**
719 * @brief Find a matching node in data tree for a diff node.
720 *
721 * @param[in] first_node First sibling in the data tree.
722 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200723 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200724 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200725static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200726lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
727{
728 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
729 /* try to find the exact instance */
730 lyd_find_sibling_first(first_node, diff_node, match_p);
731 } else {
732 /* try to simply find the node, there cannot be more instances */
733 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
734 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200735}
736
737/**
738 * @brief Learn operation of a diff node.
739 *
740 * @param[in] diff_node Diff node.
741 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200742 * @return LY_ERR value.
743 */
744static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200745lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200746{
747 struct lyd_meta *meta = NULL;
748 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200749 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200750 int dynamic;
751
752 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
753 LY_LIST_FOR(diff_parent->meta, meta) {
754 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
755 str = lyd_meta2str(meta, &dynamic);
756 assert(!dynamic);
757 if ((str[0] == 'r') && (diff_parent != diff_node)) {
758 /* we do not care about this operation if it's in our parent */
759 continue;
760 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200761 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200762 break;
763 }
764 }
765 if (meta) {
766 break;
767 }
768 }
769 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
770
Michal Vaskod59035b2020-07-08 12:00:06 +0200771 return LY_SUCCESS;
772}
773
774/**
775 * @brief Insert a diff node into a data tree.
776 *
777 * @param[in,out] first_node First sibling of the data tree.
778 * @param[in] parent_node Data tree sibling parent node.
779 * @param[in] new_node Node to insert.
780 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
781 * instance will be inserted at the first position.
782 * @return err_info, NULL on success.
783 */
784static LY_ERR
785lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
786 const char *key_or_value)
787{
788 LY_ERR ret;
789 struct lyd_node *anchor;
790
791 assert(new_node);
792
793 if (!*first_node) {
794 if (!parent_node) {
795 /* no parent or siblings */
796 *first_node = new_node;
797 return LY_SUCCESS;
798 }
799
800 /* simply insert into parent, no other children */
801 if (key_or_value) {
802 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
803 new_node->schema->name);
804 return LY_EINVAL;
805 }
Michal Vaskob104f112020-07-17 09:54:54 +0200806 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200807 }
808
809 assert(!(*first_node)->parent || ((struct lyd_node *)(*first_node)->parent == parent_node));
810
Michal Vaskod59035b2020-07-08 12:00:06 +0200811 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200812 /* simple insert */
813 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200814 }
815
816 if (key_or_value) {
817 /* find the anchor sibling */
818 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
819 if (ret == LY_ENOTFOUND) {
820 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
821 new_node->schema->name);
822 return LY_EINVAL;
823 } else if (ret) {
824 return ret;
825 }
826
827 /* insert after */
828 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
829 assert(new_node->prev == anchor);
830 if (*first_node == new_node) {
831 *first_node = anchor;
832 }
833 } else {
834 if ((*first_node)->schema->flags & LYS_KEY) {
835 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
836
837 /* find last key */
838 anchor = *first_node;
839 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
840 anchor = anchor->next;
841 }
842 /* insert after the last key */
843 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
844 } else {
845 /* insert at the beginning */
846 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
847 *first_node = new_node;
848 }
849 }
850
851 return LY_SUCCESS;
852}
853
854/**
855 * @brief Apply diff subtree on data tree nodes, recursively.
856 *
857 * @param[in,out] first_node First sibling of the data tree.
858 * @param[in] parent_node Parent of the first sibling.
859 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200860 * @param[in] diff_cb Optional diff callback.
861 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200862 * @return LY_ERR value.
863 */
864static LY_ERR
865lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
866 lyd_diff_cb diff_cb, void *cb_data)
867{
868 LY_ERR ret;
869 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200870 const char *str_val;
871 enum lyd_diff_op op;
872 struct lyd_meta *meta;
Michal Vaskod59035b2020-07-08 12:00:06 +0200873 int dynamic;
874 const struct ly_ctx *ctx = LYD_NODE_CTX(diff_node);
875
876 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200877 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200878
Michal Vaskoe6323f62020-07-09 15:49:02 +0200879 /* handle specific user-ordered (leaf-)lists operations separately */
880 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
881 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200882 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200883 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200884 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200885 /* duplicate the node */
886 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200887 }
888
Michal Vaskoe6323f62020-07-09 15:49:02 +0200889 /* get "key" or "value" metadata string value */
890 meta = lyd_find_meta(diff_node->meta, NULL, diff_node->schema->nodetype == LYS_LIST ? "yang:key" : "yang:value");
891 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
892 str_val = lyd_meta2str(meta, &dynamic);
893
Michal Vaskod59035b2020-07-08 12:00:06 +0200894 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200895 if (str_val[0]) {
896 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200897 } else {
898 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
899 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200900 if (dynamic) {
901 free((char *)str_val);
902 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200903 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200904 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200905 lyd_free_tree(match);
906 }
907 return ret;
908 }
909
910 goto next_iter_r;
911 }
912
913 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200914 switch (op) {
915 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200916 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200917 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200918
919 if (match->schema->nodetype & LYD_NODE_TERM) {
920 /* special case of only dflt flag change */
921 if (diff_node->flags & LYD_DEFAULT) {
922 match->flags |= LYD_DEFAULT;
923 } else {
924 match->flags &= ~LYD_DEFAULT;
925 }
926 } else {
927 /* none operation on nodes without children is redundant and hence forbidden */
928 if (!LYD_CHILD(diff_node)) {
929 LOGINT_RET(ctx);
930 }
931 }
932 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200933 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200934 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200935 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200936
937 /* insert it at the end */
938 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +0200939 if (parent_node) {
940 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200941 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200942 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200943 }
944 if (ret) {
945 lyd_free_tree(match);
946 return ret;
947 }
948
949 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200950 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200951 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200952 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200953
954 /* remove it */
955 if ((match == *first_node) && !match->parent) {
956 assert(!parent_node);
957 /* we have removed the top-level node */
958 *first_node = (*first_node)->next;
959 }
960 lyd_free_tree(match);
961
962 /* we are not going recursively in this case, the whole subtree was already deleted */
963 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200964 case LYD_DIFF_OP_REPLACE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200965 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
966
967 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200968 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200969
970 /* update its value */
971 str_val = lyd_value2str((struct lyd_node_term *)diff_node, &dynamic);
972 ret = lyd_change_term(match, str_val);
973 if (dynamic) {
974 free((char *)str_val);
975 }
976 if (ret && (ret != LY_EEXIST)) {
977 LOGINT_RET(ctx);
978 }
979
980 /* with flags */
981 match->flags = diff_node->flags;
982 break;
983 default:
984 LOGINT_RET(ctx);
985 }
986
987next_iter_r:
988 if (diff_cb) {
989 /* call callback */
990 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
991 }
992
993 /* apply diff recursively */
994 LY_LIST_FOR(LYD_CHILD(diff_node), diff_child) {
995 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
996 }
997
998 return LY_SUCCESS;
999}
1000
1001API LY_ERR
1002lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
1003 lyd_diff_cb diff_cb, void *cb_data)
1004{
1005 const struct lyd_node *root;
1006
1007 LY_LIST_FOR(diff, root) {
1008 if (mod && (lyd_owner_module(root) != mod)) {
1009 /* skip data nodes from different modules */
1010 continue;
1011 }
1012
1013 /* apply relevant nodes from the diff datatree */
1014 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1015 }
1016
1017 return LY_SUCCESS;
1018}
1019
1020API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001021lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001022{
1023 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1024}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001025
1026/**
1027 * @brief Update operations on a diff node when the new operation is NONE.
1028 *
1029 * @param[in] diff_match Node from the diff.
1030 * @param[in] cur_op Current operation of the diff node.
1031 * @param[in] src_diff Current source diff node.
1032 * @return LY_ERR value.
1033 */
1034static LY_ERR
1035lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1036{
1037 switch (cur_op) {
1038 case LYD_DIFF_OP_NONE:
1039 case LYD_DIFF_OP_CREATE:
1040 case LYD_DIFF_OP_REPLACE:
1041 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1042 /* NONE on a term means only its dflt flag was changed */
1043 diff_match->flags &= ~LYD_DEFAULT;
1044 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1045 }
1046 break;
1047 default:
1048 /* delete operation is not valid */
1049 LOGINT_RET(LYD_NODE_CTX(src_diff));
1050 }
1051
1052 return LY_SUCCESS;
1053}
1054
1055/**
1056 * @brief Remove an attribute from a node.
1057 *
1058 * @param[in] node Node with the metadata.
1059 * @param[in] name Metadata name.
1060 */
1061static void
1062lyd_diff_del_meta(struct lyd_node *node, const char *name)
1063{
1064 struct lyd_meta *meta;
1065
1066 LY_LIST_FOR(node->meta, meta) {
1067 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001068 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001069 return;
1070 }
1071 }
1072
1073 assert(0);
1074}
1075
1076/**
1077 * @brief Set a specific operation of a node. Delete the previous operation, if any.
1078 *
1079 * @param[in] node Node to change.
1080 * @param[in] op Operation to set.
1081 * @return LY_ERR value.
1082 */
1083static LY_ERR
1084lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1085{
1086 struct lyd_meta *meta;
1087
1088 LY_LIST_FOR(node->meta, meta) {
1089 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001090 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001091 break;
1092 }
1093 }
1094
Michal Vasko3a41dff2020-07-15 14:30:28 +02001095 return lyd_new_meta(node, NULL, "yang:operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001096}
1097
1098/**
1099 * @brief Update operations on a diff node when the new operation is REPLACE.
1100 *
1101 * @param[in] diff_match Node from the diff.
1102 * @param[in] cur_op Current operation of the diff node.
1103 * @param[in] src_diff Current source diff node.
1104 * @return LY_ERR value.
1105 */
1106static LY_ERR
1107lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1108{
1109 LY_ERR ret;
1110 int dynamic;
1111 const char *str_val, *meta_name;
1112 struct lyd_meta *meta;
1113 const struct lys_module *mod;
1114 const struct lyd_node_any *any;
1115
1116 /* get "yang" module for the metadata */
1117 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff_match), "yang");
1118 assert(mod);
1119
1120 switch (cur_op) {
1121 case LYD_DIFF_OP_REPLACE:
1122 case LYD_DIFF_OP_CREATE:
1123 switch (diff_match->schema->nodetype) {
1124 case LYS_LIST:
1125 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001126 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001127 * keep orig_key/orig_value (only replace oper) and replace key/value */
1128 assert(lysc_is_userordered(diff_match->schema));
1129 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1130
1131 lyd_diff_del_meta(diff_match, meta_name);
1132 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
1133 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001134 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001135 break;
1136 case LYS_LEAF:
1137 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001138 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001139 LOGINT_RET(LYD_NODE_CTX(src_diff));
1140 }
1141
1142 /* get leaf value */
1143 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1144
1145 /* modify the node value */
1146 ret = lyd_change_term(diff_match, str_val);
1147 if (dynamic) {
1148 free((char *)str_val);
1149 }
1150 if (ret) {
1151 LOGINT_RET(LYD_NODE_CTX(src_diff));
1152 }
1153
1154 /* compare values whether there is any change at all */
1155 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1156 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_match)), LY_EINT);
1157 str_val = lyd_meta2str(meta, &dynamic);
1158 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val), lydjson_resolve_prefix,
1159 NULL, LYD_JSON, NULL);
1160 if (dynamic) {
1161 free((char *)str_val);
1162 }
1163 if (!ret) {
1164 /* values are the same, remove orig-value meta and set oper to NONE */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001165 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001166 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1167 }
1168
1169 /* modify the default flag */
1170 diff_match->flags &= ~LYD_DEFAULT;
1171 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1172 break;
1173 case LYS_ANYXML:
1174 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001175 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001176 /* replaced with the exact same value, impossible */
1177 LOGINT_RET(LYD_NODE_CTX(src_diff));
1178 }
1179
1180 /* modify the node value */
1181 any = (struct lyd_node_any *)src_diff;
1182 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1183 break;
1184 default:
1185 LOGINT_RET(LYD_NODE_CTX(src_diff));
1186 }
1187 break;
1188 case LYD_DIFF_OP_NONE:
1189 /* it is moved now */
1190 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1191
1192 /* change the operation */
1193 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1194
1195 /* set orig-key and key metadata */
1196 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
1197 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001198 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001199
1200 meta = lyd_find_meta(src_diff->meta, mod, "key");
1201 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001202 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001203 break;
1204 default:
1205 /* delete operation is not valid */
1206 LOGINT_RET(LYD_NODE_CTX(src_diff));
1207 }
1208
1209 return LY_SUCCESS;
1210}
1211
1212/**
1213 * @brief Update operations in a diff node when the new operation is CREATE.
1214 *
1215 * @param[in] diff_match Node from the diff.
1216 * @param[in] cur_op Current operation of the diff node.
1217 * @param[in] src_diff Current source diff node.
1218 * @return LY_ERR value.
1219 */
1220static LY_ERR
1221lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1222{
1223 struct lyd_node *child;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001224 const char *str_val;
1225 int dynamic;
1226 LY_ERR ret;
1227
1228 switch (cur_op) {
1229 case LYD_DIFF_OP_DELETE:
1230 /* delete operation, if any */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001231 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001232 /* deleted + created -> operation NONE */
1233 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1234
1235 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1236 /* add orig-dflt metadata */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001237 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1238 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001239 }
1240 } else {
1241 assert(diff_match->schema->nodetype == LYS_LEAF);
1242 /* we deleted it, but it was created with a different value -> operation REPLACE */
1243 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1244
1245 /* current value is the previous one (meta) */
1246 str_val = lyd_value2str((struct lyd_node_term *)diff_match, &dynamic);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001247 ret = lyd_new_meta(diff_match, NULL, "yang:orig-value", str_val, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001248 if (dynamic) {
1249 free((char *)str_val);
1250 }
Michal Vasko3a41dff2020-07-15 14:30:28 +02001251 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001252
1253 /* update the value itself */
1254 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1255 ret = lyd_change_term(diff_match, str_val);
1256 if (dynamic) {
1257 free((char *)str_val);
1258 }
1259 LY_CHECK_RET(ret);
1260 }
1261
1262 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1263 /* update dflt flag itself */
1264 diff_match->flags &= ~LYD_DEFAULT;
1265 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1266 } else {
1267 /* but the operation of its children should remain DELETE */
1268 LY_LIST_FOR(LYD_CHILD(diff_match), child) {
1269 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1270 }
1271 }
1272 break;
1273 default:
1274 /* create and replace operations are not valid */
1275 LOGINT_RET(LYD_NODE_CTX(src_diff));
1276 }
1277
1278 return LY_SUCCESS;
1279}
1280
1281/**
1282 * @brief Update operations on a diff node when the new operation is DELETE.
1283 *
1284 * @param[in] diff_match Node from the diff.
1285 * @param[in] cur_op Current operation of the diff node.
1286 * @param[in] src_diff Current source diff node.
1287 * @return LY_ERR value.
1288 */
1289static LY_ERR
1290lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1291{
1292 struct lyd_node *next, *child;
1293
1294 /* we can delete only exact existing nodes */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001295 LY_CHECK_ERR_RET(lyd_compare_single(diff_match, src_diff, 0), LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001296
1297 switch (cur_op) {
1298 case LYD_DIFF_OP_CREATE:
1299 /* it was created, but then deleted -> set NONE operation */
1300 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1301
1302 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1303 /* add orig-default meta because it is expected */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001304 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1305 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001306 } else {
1307 /* keep operation for all descendants (for now) */
1308 LY_LIST_FOR(LYD_CHILD(diff_match), child) {
1309 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1310 }
1311 }
1312 break;
1313 case LYD_DIFF_OP_REPLACE:
1314 /* similar to none operation but also remove the redundant attribute */
1315 lyd_diff_del_meta(diff_match, "orig-value");
1316 /* fallthrough */
1317 case LYD_DIFF_OP_NONE:
1318 /* it was not modified, but should be deleted -> set DELETE operation */
1319 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1320
1321 /* all descendants will be deleted even without being in the diff, so remove them */
1322 LY_LIST_FOR_SAFE(LYD_CHILD(diff_match), next, child) {
1323 lyd_free_tree(child);
1324 }
1325 break;
1326 default:
1327 /* delete operation is not valid */
1328 LOGINT_RET(LYD_NODE_CTX(src_diff));
1329 }
1330
1331 return LY_SUCCESS;
1332}
1333
1334/**
1335 * @brief Check whether this diff node is redundant (does not change data).
1336 *
1337 * @param[in] diff Diff node.
1338 * @return 0 if not, non-zero if it is.
1339 */
1340static int
1341lyd_diff_is_redundant(struct lyd_node *diff)
1342{
1343 enum lyd_diff_op op;
1344 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1345 struct lyd_node *child;
1346 const struct lys_module *mod;
1347 const char *str;
1348 int dynamic;
1349
1350 assert(diff);
1351
1352 child = LYD_CHILD(diff);
1353 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff), "yang");
1354 assert(mod);
1355
1356 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001357 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001358
1359 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1360 /* check for redundant move */
1361 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1362 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1363 assert(orig_val_meta && val_meta);
1364
1365 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1366 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001367 lyd_free_meta_single(orig_val_meta);
1368 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001369 if (child) {
1370 /* change operation to NONE, we have siblings */
1371 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1372 return 0;
1373 }
1374
1375 /* redundant node, BUT !!
1376 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1377 * because the data that this is applied on should not change for the diff lifetime.
1378 * However, when we are merging 2 diffs, this conversion is actually lossy because
1379 * if the data change, the move operation can also change its meaning. In this specific
1380 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1381 */
1382 return 1;
1383 }
1384 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1385 /* check whether at least the default flags are different */
1386 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1387 assert(meta);
1388 str = lyd_meta2str(meta, &dynamic);
1389 assert(!dynamic);
1390
1391 /* if previous and current dflt flags are the same, this node is redundant */
1392 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1393 return 1;
1394 }
1395 return 0;
1396 }
1397
1398 if (!child && (op == LYD_DIFF_OP_NONE)) {
1399 return 1;
1400 }
1401
1402 return 0;
1403}
1404
1405/**
1406 * @brief Merge sysrepo diff with another diff, recursively.
1407 *
1408 * @param[in] src_diff Source diff node.
1409 * @param[in] diff_parent Current sysrepo diff parent.
1410 * @param[in] diff_cb Optional diff callback.
1411 * @param[in] cb_data User data for @p diff_cb.
1412 * @param[in,out] diff Diff root node.
1413 * @return LY_ERR value.
1414 */
1415static LY_ERR
1416lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
1417 struct lyd_node **diff)
1418{
1419 LY_ERR ret = LY_SUCCESS;
1420 struct lyd_node *child, *diff_node = NULL;
1421 enum lyd_diff_op src_op, cur_op;
1422
1423 /* get source node operation */
1424 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1425
1426 /* find an equal node in the current diff */
1427 lyd_diff_find_node(diff_parent ? LYD_CHILD(diff_parent) : *diff, src_diff, &diff_node);
1428
1429 if (diff_node) {
1430 /* get target (current) operation */
1431 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1432
1433 /* merge operations */
1434 switch (src_op) {
1435 case LYD_DIFF_OP_REPLACE:
1436 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1437 break;
1438 case LYD_DIFF_OP_CREATE:
1439 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff);
1440 break;
1441 case LYD_DIFF_OP_DELETE:
1442 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1443 break;
1444 case LYD_DIFF_OP_NONE:
1445 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1446 break;
1447 default:
1448 LOGINT_RET(LYD_NODE_CTX(src_diff));
1449 }
1450 if (ret) {
1451 LOGERR(LYD_NODE_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
1452 return ret;
1453 }
1454
1455 if (diff_cb) {
1456 /* call callback */
1457 LY_CHECK_RET(diff_cb(diff_node, NULL, cb_data));
1458 }
1459
1460 /* update diff parent */
1461 diff_parent = diff_node;
1462
1463 /* merge src_diff recursively */
1464 LY_LIST_FOR(LYD_CHILD(src_diff), child) {
1465 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, diff));
1466 }
1467 } else {
1468 /* add new diff node with all descendants */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001469 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE, &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001470
1471 /* insert node into diff if not already */
1472 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001473 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001474 }
1475
1476 /* update operation */
1477 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1478
1479 if (diff_cb) {
1480 /* call callback */
1481 LY_CHECK_RET(diff_cb(diff_node, NULL, cb_data));
1482 }
1483
1484 /* update diff parent */
1485 diff_parent = diff_node;
1486 }
1487
1488 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001489 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001490 if (diff_parent == *diff) {
1491 *diff = (*diff)->next;
1492 }
1493 lyd_free_tree(diff_parent);
1494 }
1495
1496 return LY_SUCCESS;
1497}
1498
1499API LY_ERR
1500lyd_diff_merge_module(const struct lyd_node *src_diff, const struct lys_module *mod, lyd_diff_cb diff_cb, void *cb_data,
1501 struct lyd_node **diff)
1502{
1503 const struct lyd_node *src_root;
1504
1505 LY_LIST_FOR(src_diff, src_root) {
1506 if (mod && (lyd_owner_module(src_root) != mod)) {
1507 /* skip data nodes from different modules */
1508 continue;
1509 }
1510
1511 /* apply relevant nodes from the diff datatree */
1512 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, diff));
1513 }
1514
1515 return LY_SUCCESS;
1516}
1517
1518API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001519lyd_diff_merge_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001520{
1521 return lyd_diff_merge_module(src_diff, NULL, NULL, NULL, diff);
1522}
Michal Vasko4231fb62020-07-13 13:54:47 +02001523
1524static LY_ERR
1525lyd_diff_reverse_value(struct lyd_node *leaf, const struct lys_module *mod)
1526{
1527 LY_ERR ret = LY_SUCCESS;
1528 struct lyd_meta *meta;
1529 const char *val1 = NULL, *val2 = NULL;
1530 int dyn1 = 0, dyn2 = 0, flags;
1531
1532 meta = lyd_find_meta(leaf->meta, mod, "orig-value");
1533 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(leaf)), LY_EINT);
1534
1535 /* orig-value */
1536 val1 = lyd_meta2str(meta, &dyn1);
1537
1538 /* current value */
1539 val2 = lyd_value2str((struct lyd_node_term *)leaf, &dyn2);
1540
1541 /* switch values, keep default flag */
1542 flags = leaf->flags;
1543 LY_CHECK_GOTO(ret = lyd_change_term(leaf, val1), cleanup);
1544 leaf->flags = flags;
1545 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1546
1547cleanup:
1548 if (dyn1) {
1549 free((char *)val1);
1550 }
1551 if (dyn2) {
1552 free((char *)val2);
1553 }
1554 return ret;
1555}
1556
1557static LY_ERR
1558lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1559{
1560 struct lyd_meta *meta;
1561 const char *val;
1562 int dyn, flag1, flag2;
1563
1564 meta = lyd_find_meta(node->meta, mod, "orig-default");
1565 if (!meta) {
1566 /* default flag did not change */
1567 return LY_SUCCESS;
1568 }
1569
1570 /* orig-default */
1571 val = lyd_meta2str(meta, &dyn);
1572 assert(!dyn);
1573 if (!strcmp(val, "true")) {
1574 flag1 = LYD_DEFAULT;
1575 } else {
1576 flag1 = 0;
1577 }
1578
1579 /* current default */
1580 flag2 = node->flags & LYD_DEFAULT;
1581
1582 /* switch defaults */
1583 node->flags &= ~LYD_DEFAULT;
1584 node->flags |= flag1;
1585 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1586
1587 return LY_SUCCESS;
1588}
1589
1590static LY_ERR
1591lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1592{
1593 LY_ERR ret = LY_SUCCESS;
1594 struct lyd_meta *meta1, *meta2;
1595 const char *val1 = NULL, *val2 = NULL;
1596 int dyn1 = 0, dyn2 = 0;
1597
1598 meta1 = lyd_find_meta(node->meta, mod, name1);
1599 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
1600
1601 meta2 = lyd_find_meta(node->meta, mod, name2);
1602 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
1603
1604 /* value1 */
1605 val1 = lyd_meta2str(meta1, &dyn1);
1606
1607 /* value2 */
1608 val2 = lyd_meta2str(meta2, &dyn2);
1609
1610 /* switch values */
1611 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1612 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1613
1614cleanup:
1615 if (dyn1) {
1616 free((char *)val1);
1617 }
1618 if (dyn2) {
1619 free((char *)val2);
1620 }
1621 return ret;
1622}
1623
1624API LY_ERR
1625lyd_diff_reverse(const struct lyd_node *src_diff, struct lyd_node **diff)
1626{
1627 LY_ERR ret = LY_SUCCESS;
1628 const struct lys_module *mod;
1629 struct lyd_node *root, *next, *elem;
1630 enum lyd_diff_op op;
1631
1632 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1633
1634 if (!src_diff) {
1635 *diff = NULL;
1636 return LY_SUCCESS;
1637 }
1638
1639 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001640 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001641
1642 /* find module with metadata needed for later */
1643 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(src_diff), "yang");
1644 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_NODE_CTX(src_diff)); ret = LY_EINT, cleanup);
1645
1646 LY_LIST_FOR(*diff, root) {
1647 LYD_TREE_DFS_BEGIN(root, next, elem) {
1648 /* find operation attribute, if any */
1649 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
1650
1651 switch (op) {
1652 case LYD_DIFF_OP_CREATE:
1653 /* reverse create to delete */
1654 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
1655 break;
1656 case LYD_DIFF_OP_DELETE:
1657 /* reverse delete to create */
1658 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
1659 break;
1660 case LYD_DIFF_OP_REPLACE:
1661 switch (elem->schema->nodetype) {
1662 case LYS_LEAF:
1663 /* leaf value change */
1664 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1665 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1666 break;
1667 case LYS_LEAFLIST:
1668 /* leaf-list move */
1669 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1670 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1671 break;
1672 case LYS_LIST:
1673 /* list move */
1674 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1675 break;
1676 default:
1677 LOGINT(LYD_NODE_CTX(src_diff));
1678 ret = LY_EINT;
1679 goto cleanup;
1680 }
1681 break;
1682 case LYD_DIFF_OP_NONE:
1683 switch (elem->schema->nodetype) {
1684 case LYS_LEAF:
1685 case LYS_LEAFLIST:
1686 /* default flag change */
1687 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1688 break;
1689 default:
1690 /* nothing to do */
1691 break;
1692 }
1693 break;
1694 default:
1695 /* nothing to do */
1696 break;
1697 }
1698
1699 LYD_TREE_DFS_END(root, next, elem);
1700 }
1701 }
1702
1703cleanup:
1704 if (ret) {
1705 lyd_free_siblings(*diff);
1706 *diff = NULL;
1707 }
1708 return ret;
1709}