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