1 /**
2 * @file printer_xml.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief XML printer for libyang data structure
5 *
6 * Copyright (c) 2015 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
15 #define _GNU_SOURCE
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <assert.h>
22 #include <inttypes.h>
23
24 #include "common.h"
25 #include "parser.h"
26 #include "printer.h"
27 #include "xml_internal.h"
28 #include "tree_data.h"
29 #include "tree_schema.h"
30 #include "resolve.h"
31 #include "tree_internal.h"
32
33 #define INDENT ""
34 #define LEVEL (level ? level*2-2 : 0)
35
36 struct mlist {
37 struct mlist *next;
38 struct lys_module *module;
39 uint8_t printed;
40 };
41
42 static int
modlist_add(struct mlist ** mlist,const struct lys_module * mod)43 modlist_add(struct mlist **mlist, const struct lys_module *mod)
44 {
45 struct mlist *iter;
46
47 for (iter = *mlist; iter; iter = iter->next) {
48 if (mod == iter->module) {
49 break;
50 }
51 }
52
53 if (!iter) {
54 iter = malloc(sizeof *iter);
55 LY_CHECK_ERR_RETURN(!iter, LOGMEM(mod->ctx), EXIT_FAILURE);
56 iter->next = *mlist;
57 iter->module = (struct lys_module *)mod;
58 iter->printed = 0;
59 *mlist = iter;
60 }
61
62 return EXIT_SUCCESS;
63 }
64
65 static void
free_mlist(struct mlist ** mlist)66 free_mlist(struct mlist **mlist) {
67 struct mlist *miter;
68 while (*mlist) {
69 miter = *mlist;
70 *mlist = miter->next;
71 free(miter);
72 }
73 }
74
75 inline static int
is_netconf_filter(const struct lyd_node * node)76 is_netconf_filter(const struct lyd_node *node)
77 {
78 return !strcmp(node->schema->name, "filter")
79 && (!strcmp(node->schema->module->name, "ietf-netconf")
80 || !strcmp(node->schema->module->name, "notifications"));
81 }
82
83 inline static int
is_type_or_select(const struct lyd_attr * attr)84 is_type_or_select(const struct lyd_attr *attr)
85 {
86 return (!strcmp(attr->name, "type") || !strcmp(attr->name, "select"));
87 }
88
89 static void
xml_print_ns(struct lyout * out,const struct lyd_node * node,struct mlist ** mlist,int options)90 xml_print_ns(struct lyout *out, const struct lyd_node *node, struct mlist **mlist, int options)
91 {
92 struct lyd_node *next, *cur, *node2;
93 struct lyd_attr *attr;
94 const struct lys_module *wdmod = NULL;
95 struct mlist *miter;
96 int r;
97
98 assert(out);
99 assert(node);
100
101 /* add node attribute modules */
102 for (attr = node->attr; attr; attr = attr->next) {
103 if (is_netconf_filter(node) && is_type_or_select(attr)) {
104 /* exception for NETCONF's filter attributes */
105 continue;
106 } else {
107 r = modlist_add(mlist, lys_main_module(attr->annotation->module));
108 }
109 if (r) {
110 goto print;
111 }
112 }
113
114 /* add node children nodes and attribute modules */
115 switch (node->schema->nodetype) {
116 case LYS_LEAFLIST:
117 case LYS_LEAF:
118 if (node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) {
119 /* get with-defaults module and print its namespace */
120 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
121 if (wdmod && modlist_add(mlist, wdmod)) {
122 goto print;
123 }
124 }
125 break;
126 case LYS_CONTAINER:
127 case LYS_LIST:
128 case LYS_RPC:
129 case LYS_ACTION:
130 case LYS_NOTIF:
131 if (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG)) {
132 /* get with-defaults module and print its namespace */
133 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
134 if (wdmod && modlist_add(mlist, wdmod)) {
135 goto print;
136 }
137 }
138
139 LY_TREE_FOR(node->child, node2) {
140 LY_TREE_DFS_BEGIN(node2, next, cur) {
141 for (attr = cur->attr; attr; attr = attr->next) {
142 if (is_netconf_filter(cur) && is_type_or_select(attr)) {
143 /* exception for NETCONF's filter attributes */
144 continue;
145 } else {
146 r = modlist_add(mlist, lys_main_module(attr->annotation->module));
147 }
148 if (r) {
149 goto print;
150 }
151 }
152 LY_TREE_DFS_END(node2, next, cur)}
153 }
154 break;
155 default:
156 break;
157 }
158
159 print:
160 /* print used namespaces */
161 miter = *mlist;
162 while (miter) {
163
164 if (!miter->printed) {
165 ly_print(out, " xmlns:%s=\"%s\"", miter->module->prefix, miter->module->ns);
166 miter->printed = 1;
167 }
168 miter = miter->next;
169 }
170 }
171
172 static int
xml_print_attrs(struct lyout * out,const struct lyd_node * node,int options)173 xml_print_attrs(struct lyout *out, const struct lyd_node *node, int options)
174 {
175 struct lyd_attr *attr;
176 const char **prefs, **nss;
177 const char *xml_expr = NULL, *mod_name;
178 char *ident_val;
179 uint32_t ns_count, i;
180 int is_nc_filter = 0;
181 size_t len;
182 const struct lys_module *wdmod = NULL;
183
184 LY_PRINT_SET;
185
186 /* with-defaults */
187 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
188 if ((node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) ||
189 (!node->dflt && (options & LYP_WD_ALL_TAG) && lyd_wd_default((struct lyd_node_leaf_list *)node))) {
190 /* we have implicit OR explicit default node */
191 /* get with-defaults module */
192 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
193 if (wdmod) {
194 /* print attribute only if context include with-defaults schema */
195 ly_print(out, " %s:default=\"true\"", wdmod->prefix);
196 }
197 }
198 }
199 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
200 if (is_netconf_filter(node)) {
201 is_nc_filter = 1;
202 }
203
204 for (attr = node->attr; attr; attr = attr->next) {
205 ident_val = NULL;
206 if (attr->value_str && ((attr->value_type == LY_TYPE_INST) || (attr->value_type == LY_TYPE_IDENT)
207 || (!strcmp(attr->annotation->module->name, "yang") && !strcmp(attr->name, "key")))) {
208 if (attr->value_type == LY_TYPE_IDENT) {
209 ident_val = strchr(attr->value_str, ':');
210 assert(ident_val);
211 len = ident_val - attr->value_str;
212 mod_name = attr->annotation->module->name;
213 if (!strncmp(attr->value_str, mod_name, len) && !mod_name[len]) {
214 /* skip local identity prefix */
215 ++ident_val;
216 goto normal_print;
217 }
218
219 /* print the prefix correctly below */
220 ident_val = NULL;
221 }
222
223 xml_expr = transform_json2xml(node->schema->module, attr->value_str, 1, &prefs, &nss, &ns_count);
224 if (!xml_expr) {
225 /* error */
226 return EXIT_FAILURE;
227 }
228
229 for (i = 0; i < ns_count; ++i) {
230 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
231 }
232 free(prefs);
233 free(nss);
234 }
235
236 normal_print:
237 if (is_nc_filter && is_type_or_select(attr)) {
238 /* exception for NETCONF's filter's attributes */
239 if (!strcmp(attr->name, "select")) {
240 /* xpath content, we have to convert the JSON format into XML first */
241 xml_expr = transform_json2xml(node->schema->module, attr->value_str, 0, &prefs, &nss, &ns_count);
242 if (!xml_expr) {
243 /* error */
244 return EXIT_FAILURE;
245 }
246
247 for (i = 0; i < ns_count; ++i) {
248 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
249 }
250 free(prefs);
251 free(nss);
252 }
253 ly_print(out, " %s=\"", attr->name);
254 } else {
255 ly_print(out, " %s:%s=\"", attr->annotation->module->prefix, attr->name);
256 }
257
258 switch (attr->value_type) {
259 case LY_TYPE_BINARY:
260 case LY_TYPE_STRING:
261 case LY_TYPE_BITS:
262 case LY_TYPE_ENUM:
263 case LY_TYPE_BOOL:
264 case LY_TYPE_DEC64:
265 case LY_TYPE_INT8:
266 case LY_TYPE_INT16:
267 case LY_TYPE_INT32:
268 case LY_TYPE_INT64:
269 case LY_TYPE_UINT8:
270 case LY_TYPE_UINT16:
271 case LY_TYPE_UINT32:
272 case LY_TYPE_UINT64:
273 case LY_TYPE_INST:
274 if (attr->value_str) {
275 /* xml_expr can contain transformed xpath */
276 lyxml_dump_text(out, xml_expr ? xml_expr : attr->value_str, LYXML_DATA_ATTR);
277 }
278 break;
279 case LY_TYPE_IDENT:
280 if (attr->value_str) {
281 if (ident_val) {
282 lyxml_dump_text(out, ident_val, LYXML_DATA_ATTR);
283 } else {
284 /* xml_expr can contain transformed xpath */
285 lyxml_dump_text(out, xml_expr ? xml_expr : attr->value_str, LYXML_DATA_ATTR);
286 }
287 }
288 break;
289 /* LY_TYPE_LEAFREF not allowed */
290 case LY_TYPE_EMPTY:
291 break;
292
293 default:
294 /* error */
295 LOGINT(node->schema->module->ctx);
296 return EXIT_FAILURE;
297 }
298
299 ly_print(out, "\"");
300
301 if (xml_expr) {
302 lydict_remove(node->schema->module->ctx, xml_expr);
303 xml_expr = NULL;
304 }
305 }
306
307 LY_PRINT_RET(node->schema->module->ctx);
308 }
309
310 static int
xml_print_leaf(struct lyout * out,int level,const struct lyd_node * node,int toplevel,int options)311 xml_print_leaf(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
312 {
313 const struct lyd_node_leaf_list *leaf = (struct lyd_node_leaf_list *)node, *iter;
314 const struct lys_type *type;
315 struct lys_tpdf *tpdf;
316 const char *ns, *mod_name;
317 const char **prefs, **nss;
318 const char *xml_expr;
319 uint32_t ns_count, i;
320 LY_DATA_TYPE datatype;
321 char *p;
322 size_t len;
323 enum int_log_opts prev_ilo;
324 struct mlist *mlist = NULL;
325
326 LY_PRINT_SET;
327
328 if (toplevel || !node->parent || nscmp(node, node->parent)) {
329 /* print "namespace" */
330 ns = lyd_node_module(node)->ns;
331 ly_print(out, "%*s<%s xmlns=\"%s\"", LEVEL, INDENT, node->schema->name, ns);
332 } else {
333 ly_print(out, "%*s<%s", LEVEL, INDENT, node->schema->name);
334 }
335
336 if (toplevel) {
337 xml_print_ns(out, node, &mlist, options);
338 free_mlist(&mlist);
339 }
340
341 if (xml_print_attrs(out, node, options)) {
342 return EXIT_FAILURE;
343 }
344 datatype = leaf->value_type;
345
346 printvalue:
347 switch (datatype) {
348 case LY_TYPE_STRING:
349 ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
350 type = lyd_leaf_type((struct lyd_node_leaf_list *)leaf);
351 ly_ilo_restore(NULL, prev_ilo, NULL, 0);
352 if (type) {
353 for (tpdf = type->der;
354 tpdf->module && (strcmp(tpdf->name, "xpath1.0") || strcmp(tpdf->module->name, "ietf-yang-types"));
355 tpdf = tpdf->type.der);
356 /* special handling of ietf-yang-types xpath1.0 */
357 if (tpdf->module) {
358 /* avoid code duplication - use instance-identifier printer which gets necessary namespaces to print */
359 datatype = LY_TYPE_INST;
360 goto printvalue;
361 }
362 }
363 /* fallthrough */
364 case LY_TYPE_BINARY:
365 case LY_TYPE_BITS:
366 case LY_TYPE_ENUM:
367 case LY_TYPE_BOOL:
368 case LY_TYPE_UNION:
369 case LY_TYPE_DEC64:
370 case LY_TYPE_INT8:
371 case LY_TYPE_INT16:
372 case LY_TYPE_INT32:
373 case LY_TYPE_INT64:
374 case LY_TYPE_UINT8:
375 case LY_TYPE_UINT16:
376 case LY_TYPE_UINT32:
377 case LY_TYPE_UINT64:
378 if (!leaf->value_str || !leaf->value_str[0]) {
379 ly_print(out, "/>");
380 } else {
381 ly_print(out, ">");
382 lyxml_dump_text(out, leaf->value_str, LYXML_DATA_ELEM);
383 ly_print(out, "</%s>", node->schema->name);
384 }
385 break;
386
387 case LY_TYPE_IDENT:
388 if (!leaf->value_str || !leaf->value_str[0]) {
389 ly_print(out, "/>");
390 break;
391 }
392 p = strchr(leaf->value_str, ':');
393 assert(p);
394 len = p - leaf->value_str;
395 mod_name = leaf->schema->module->name;
396 if (!strncmp(leaf->value_str, mod_name, len) && !mod_name[len]) {
397 ly_print(out, ">");
398 lyxml_dump_text(out, ++p, LYXML_DATA_ELEM);
399 ly_print(out, "</%s>", node->schema->name);
400 } else {
401 /* avoid code duplication - use instance-identifier printer which gets necessary namespaces to print */
402 datatype = LY_TYPE_INST;
403 goto printvalue;
404 }
405 break;
406 case LY_TYPE_INST:
407 xml_expr = transform_json2xml(node->schema->module, ((struct lyd_node_leaf_list *)node)->value_str, 1,
408 &prefs, &nss, &ns_count);
409 if (!xml_expr) {
410 /* error */
411 return EXIT_FAILURE;
412 }
413
414 for (i = 0; i < ns_count; ++i) {
415 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
416 }
417 free(prefs);
418 free(nss);
419
420 if (xml_expr[0]) {
421 ly_print(out, ">");
422 lyxml_dump_text(out, xml_expr, LYXML_DATA_ELEM);
423 ly_print(out, "</%s>", node->schema->name);
424 } else {
425 ly_print(out, "/>");
426 }
427 lydict_remove(node->schema->module->ctx, xml_expr);
428 break;
429
430 case LY_TYPE_LEAFREF:
431 iter = (struct lyd_node_leaf_list *)leaf->value.leafref;
432 while (iter && (iter->value_type == LY_TYPE_LEAFREF)) {
433 iter = (struct lyd_node_leaf_list *)iter->value.leafref;
434 }
435 if (!iter) {
436 /* unresolved and invalid, but we can learn the correct type anyway */
437 type = lyd_leaf_type((struct lyd_node_leaf_list *)leaf);
438 if (!type) {
439 /* error */
440 return EXIT_FAILURE;
441 }
442 datatype = type->base;
443 } else {
444 datatype = iter->value_type;
445 }
446 goto printvalue;
447
448 case LY_TYPE_EMPTY:
449 case LY_TYPE_UNKNOWN:
450 /* treat <edit-config> node without value as empty */
451 ly_print(out, "/>");
452 break;
453
454 default:
455 /* error */
456 LOGINT(node->schema->module->ctx);
457 return EXIT_FAILURE;
458 }
459
460 if (level) {
461 ly_print(out, "\n");
462 }
463
464 LY_PRINT_RET(node->schema->module->ctx);
465 }
466
467 static int
xml_print_container(struct lyout * out,int level,const struct lyd_node * node,int toplevel,int options)468 xml_print_container(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
469 {
470 struct lyd_node *child;
471 const char *ns;
472 struct mlist *mlist = NULL;
473
474 LY_PRINT_SET;
475
476 if (toplevel || !node->parent || nscmp(node, node->parent)) {
477 /* print "namespace" */
478 ns = lyd_node_module(node)->ns;
479 ly_print(out, "%*s<%s xmlns=\"%s\"", LEVEL, INDENT, node->schema->name, ns);
480 } else {
481 ly_print(out, "%*s<%s", LEVEL, INDENT, node->schema->name);
482 }
483
484 if (toplevel) {
485 xml_print_ns(out, node, &mlist, options);
486 free_mlist(&mlist);
487 }
488
489 if (xml_print_attrs(out, node, options)) {
490 return EXIT_FAILURE;
491 }
492
493 if (!node->child) {
494 ly_print(out, "/>%s", level ? "\n" : "");
495 goto finish;
496 }
497 ly_print(out, ">%s", level ? "\n" : "");
498
499 LY_TREE_FOR(node->child, child) {
500 if (xml_print_node(out, level ? level + 1 : 0, child, 0, options)) {
501 return EXIT_FAILURE;
502 }
503 }
504
505 ly_print(out, "%*s</%s>%s", LEVEL, INDENT, node->schema->name, level ? "\n" : "");
506
507 finish:
508 LY_PRINT_RET(node->schema->module->ctx);
509 }
510
511 static int
xml_print_list(struct lyout * out,int level,const struct lyd_node * node,int is_list,int toplevel,int options)512 xml_print_list(struct lyout *out, int level, const struct lyd_node *node, int is_list, int toplevel, int options)
513 {
514 struct lyd_node *child;
515 const char *ns;
516 struct mlist *mlist = NULL;
517
518 LY_PRINT_SET;
519
520 if (is_list) {
521 /* list print */
522 if (toplevel || !node->parent || nscmp(node, node->parent)) {
523 /* print "namespace" */
524 ns = lyd_node_module(node)->ns;
525 ly_print(out, "%*s<%s xmlns=\"%s\"", LEVEL, INDENT, node->schema->name, ns);
526 } else {
527 ly_print(out, "%*s<%s", LEVEL, INDENT, node->schema->name);
528 }
529
530 if (toplevel) {
531 xml_print_ns(out, node, &mlist, options);
532 free_mlist(&mlist);
533 }
534 if (xml_print_attrs(out, node, options)) {
535 return EXIT_FAILURE;
536 }
537
538 if (!node->child) {
539 ly_print(out, "/>%s", level ? "\n" : "");
540 goto finish;
541 }
542 ly_print(out, ">%s", level ? "\n" : "");
543
544 LY_TREE_FOR(node->child, child) {
545 if (xml_print_node(out, level ? level + 1 : 0, child, 0, options)) {
546 return EXIT_FAILURE;
547 }
548 }
549
550 ly_print(out, "%*s</%s>%s", LEVEL, INDENT, node->schema->name, level ? "\n" : "");
551 } else {
552 /* leaf-list print */
553 xml_print_leaf(out, level, node, toplevel, options);
554 }
555
556 finish:
557 LY_PRINT_RET(node->schema->module->ctx);
558 }
559
560 static int
xml_print_anydata(struct lyout * out,int level,const struct lyd_node * node,int toplevel,int options)561 xml_print_anydata(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
562 {
563 char *buf;
564 struct lyd_node_anydata *any = (struct lyd_node_anydata *)node;
565 struct lyd_node *iter;
566 const char *ns;
567 struct mlist *mlist = NULL;
568
569 LY_PRINT_SET;
570
571 if (toplevel || !node->parent || nscmp(node, node->parent)) {
572 /* print "namespace" */
573 ns = lyd_node_module(node)->ns;
574 ly_print(out, "%*s<%s xmlns=\"%s\"", LEVEL, INDENT, node->schema->name, ns);
575 } else {
576 ly_print(out, "%*s<%s", LEVEL, INDENT, node->schema->name);
577 }
578
579 if (toplevel) {
580 xml_print_ns(out, node, &mlist, options);
581 }
582 if (xml_print_attrs(out, node, options)) {
583 return EXIT_FAILURE;
584 }
585 if (!(void*)any->value.tree || (any->value_type == LYD_ANYDATA_CONSTSTRING && !any->value.str[0])) {
586 /* no content */
587 ly_print(out, "/>%s", level ? "\n" : "");
588 } else {
589 if (any->value_type == LYD_ANYDATA_LYB) {
590 /* parse into a data tree */
591 ly_errno = 0;
592 iter = lyd_parse_mem(node->schema->module->ctx, any->value.mem, LYD_LYB, LYD_OPT_DATA | LYD_OPT_STRICT
593 | LYD_OPT_TRUSTED);
594 if (!ly_errno) {
595 /* successfully parsed */
596 free(any->value.mem);
597 any->value_type = LYD_ANYDATA_DATATREE;
598 any->value.tree = iter;
599 }
600 }
601 if (any->value_type == LYD_ANYDATA_DATATREE) {
602 /* print namespaces in the anydata data tree */
603 LY_TREE_FOR(any->value.tree, iter) {
604 xml_print_ns(out, iter, &mlist, options);
605 }
606 }
607 /* close opening tag ... */
608 ly_print(out, ">");
609 free_mlist(&mlist);
610 /* ... and print anydata content */
611 switch (any->value_type) {
612 case LYD_ANYDATA_CONSTSTRING:
613 lyxml_dump_text(out, any->value.str, LYXML_DATA_ELEM);
614 break;
615 case LYD_ANYDATA_DATATREE:
616 if (any->value.tree) {
617 if (level) {
618 ly_print(out, "\n");
619 }
620 LY_TREE_FOR(any->value.tree, iter) {
621 if (xml_print_node(out, level ? level + 1 : 0, iter, 0, (options & ~(LYP_WITHSIBLINGS | LYP_NETCONF)))) {
622 return EXIT_FAILURE;
623 }
624 }
625 }
626 break;
627 case LYD_ANYDATA_XML:
628 lyxml_print_mem(&buf, any->value.xml, (level ? LYXML_PRINT_FORMAT | LYXML_PRINT_NO_LAST_NEWLINE : 0)
629 | LYXML_PRINT_SIBLINGS);
630 ly_print(out, "%s%s", level ? "\n" : "", buf);
631 free(buf);
632 break;
633 case LYD_ANYDATA_SXML:
634 /* print without escaping special characters */
635 ly_print(out, "%s", any->value.str);
636 break;
637 case LYD_ANYDATA_JSON:
638 case LYD_ANYDATA_LYB:
639 /* JSON format is not supported (LYB failed to be converted) */
640 LOGWRN(node->schema->module->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
641 break;
642 case LYD_ANYDATA_STRING:
643 case LYD_ANYDATA_SXMLD:
644 case LYD_ANYDATA_JSOND:
645 case LYD_ANYDATA_LYBD:
646 /* dynamic strings are used only as input parameters */
647 assert(0);
648 break;
649 }
650
651 /* closing tag */
652 ly_print(out, "</%s>%s", node->schema->name, level ? "\n" : "");
653 }
654
655 LY_PRINT_RET(node->schema->module->ctx);
656 }
657
658 int
xml_print_node(struct lyout * out,int level,const struct lyd_node * node,int toplevel,int options)659 xml_print_node(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
660 {
661 int ret = EXIT_SUCCESS;
662
663 if (!lyd_node_should_print(node, options)) {
664 /* wd says do not print */
665 return EXIT_SUCCESS;
666 }
667
668 switch (node->schema->nodetype) {
669 case LYS_NOTIF:
670 case LYS_RPC:
671 case LYS_ACTION:
672 case LYS_CONTAINER:
673 ret = xml_print_container(out, level, node, toplevel, options);
674 break;
675 case LYS_LEAF:
676 ret = xml_print_leaf(out, level, node, toplevel, options);
677 break;
678 case LYS_LEAFLIST:
679 ret = xml_print_list(out, level, node, 0, toplevel, options);
680 break;
681 case LYS_LIST:
682 ret = xml_print_list(out, level, node, 1, toplevel, options);
683 break;
684 case LYS_ANYXML:
685 case LYS_ANYDATA:
686 ret = xml_print_anydata(out, level, node, toplevel, options);
687 break;
688 default:
689 LOGINT(node->schema->module->ctx);
690 ret = EXIT_FAILURE;
691 break;
692 }
693
694 return ret;
695 }
696
697 int
xml_print_data(struct lyout * out,const struct lyd_node * root,int options)698 xml_print_data(struct lyout *out, const struct lyd_node *root, int options)
699 {
700 const struct lyd_node *node, *next;
701 struct lys_node *parent = NULL;
702 int level, action_input = 0;
703
704 LY_PRINT_SET;
705
706 if (!root) {
707 if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
708 ly_print(out, "");
709 }
710 goto finish;
711 }
712
713 level = (options & LYP_FORMAT ? 1 : 0);
714
715 if (options & LYP_NETCONF) {
716 if (root->schema->nodetype != LYS_RPC) {
717 /* learn whether we are printing an action */
718 LY_TREE_DFS_BEGIN(root, next, node) {
719 if (node->schema->nodetype == LYS_ACTION) {
720 break;
721 }
722 LY_TREE_DFS_END(root, next, node);
723 }
724 } else {
725 node = root;
726 }
727
728 if (node) {
729 if ((node->schema->nodetype & (LYS_LIST | LYS_CONTAINER | LYS_RPC | LYS_NOTIF | LYS_ACTION)) && node->child) {
730 for (parent = lys_parent(node->child->schema); parent && (parent->nodetype == LYS_USES); parent = lys_parent(parent));
731 }
732 if (parent && (parent->nodetype == LYS_OUTPUT)) {
733 /* rpc/action output - skip the container */
734 root = node->child;
735 } else if (node->schema->nodetype == LYS_ACTION) {
736 /* action input - print top-level action element */
737 action_input = 1;
738 }
739 }
740 }
741
742 if (action_input) {
743 ly_print(out, "%*s<action xmlns=\"%s\">%s", LEVEL, INDENT, LY_NSYANG, level ? "\n" : "");
744 if (level) {
745 ++level;
746 }
747 }
748
749 /* content */
750 LY_TREE_FOR(root, node) {
751 if (xml_print_node(out, level, node, 1, options)) {
752 return EXIT_FAILURE;
753 }
754 if (!(options & LYP_WITHSIBLINGS)) {
755 break;
756 }
757 }
758
759 if (action_input) {
760 if (level) {
761 --level;
762 }
763 ly_print(out, "%*s</action>%s", LEVEL, INDENT, level ? "\n" : "");
764 }
765
766 finish:
767 ly_print_flush(out);
768
769 LY_PRINT_RET(NULL);
770 }
771
772