blob: 1dce0961ee606ab7d4959a9c66cc297a753341b9 [file] [log] [blame]
Radek Krejcie7b95092019-05-15 11:03:07 +02001/**
2 * @file printer_xml.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @author Radek Krejci <rkrejci@cesnet.cz>
5 * @brief XML printer for libyang data structure
6 *
7 * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
16#include "common.h"
17
18#include <stdlib.h>
19#include <string.h>
20
21#include "log.h"
22#include "plugins_types.h"
23#include "printer_data.h"
24#include "printer_internal.h"
25#include "tree.h"
26#include "tree_data.h"
27#include "tree_schema.h"
28#include "xml.h"
29
30/**
31 * @brief XML printer context.
32 */
33struct xmlpr_ctx {
34 struct lyout *out; /**< output specification */
35 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
36 int options; /**< [Data printer flags](@ref dataprinterflags) */
37 int toplevel; /**< top-level flag */
38};
39
40#define LEVEL ctx->level /**< current level */
41#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
42#define LEVEL_INC if (LEVEL) {LEVEL++;} /**< increase indentation level */
43#define LEVEL_DEC if (LEVEL) {LEVEL--;} /**< decrease indentation level */
44
45/**
46 * TODO
47 */
48struct mlist {
49 struct mlist *next;
50 struct lys_module *module;
51} *mlist = NULL, *mlist_new;
52
Radek Krejcie7b95092019-05-15 11:03:07 +020053static LY_ERR
54modlist_add(struct mlist **mlist, const struct lys_module *mod)
55{
56 struct mlist *iter;
57
58 for (iter = *mlist; iter; iter = iter->next) {
59 if (mod == iter->module) {
60 break;
61 }
62 }
63
64 if (!iter) {
65 iter = malloc(sizeof *iter);
66 LY_CHECK_ERR_RET(!iter, LOGMEM(mod->ctx), LY_EMEM);
67 iter->next = *mlist;
68 iter->module = (struct lys_module *)mod;
69 *mlist = iter;
70 }
71
72 return LY_SUCCESS;
73}
Radek Krejcie7b95092019-05-15 11:03:07 +020074
75/**
76 * TODO
77 */
78static void
79xml_print_ns(struct xmlpr_ctx *ctx, const struct lyd_node *node)
80{
81 struct lyd_node *next, *cur, *child;
82 struct lyd_attr *attr;
Radek Krejci585f1922019-05-17 10:34:15 +020083 struct mlist *mlist = NULL, *miter;
Radek Krejcie7b95092019-05-15 11:03:07 +020084 const struct lys_module *wdmod = NULL;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010085
Radek Krejcie7b95092019-05-15 11:03:07 +020086 /* add node attribute modules */
87 for (attr = node->attr; attr; attr = attr->next) {
88 if (!strcmp(node->schema->name, "filter") &&
89 (!strcmp(node->schema->module->name, "ietf-netconf") ||
90 !strcmp(node->schema->module->name, "notifications"))) {
91 /* exception for NETCONF's filter attributes */
92 continue;
Radek Krejci28681fa2019-09-06 13:08:45 +020093 } else if (modlist_add(&mlist, attr->annotation->module)) {
Radek Krejcie7b95092019-05-15 11:03:07 +020094 goto print;
95 }
96 }
Radek Krejcie7b95092019-05-15 11:03:07 +020097
98 /* add node children nodes and attribute modules */
99 switch (node->schema->nodetype) {
100 case LYS_LEAFLIST:
101 case LYS_LEAF:
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100102 /* ietf-netconf-with-defaults namespace */
103 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG))) ||
104 ((ctx->options & LYDP_WD_ALL_TAG) && ly_is_default(node))) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200105 /* get with-defaults module and print its namespace */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100106 wdmod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
Radek Krejcie7b95092019-05-15 11:03:07 +0200107 if (wdmod && modlist_add(&mlist, wdmod)) {
108 goto print;
109 }
110 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200111 break;
112 case LYS_CONTAINER:
113 case LYS_LIST:
Radek Krejcie7b95092019-05-15 11:03:07 +0200114 case LYS_ACTION:
115 case LYS_NOTIF:
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100116 if (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200117 /* get with-defaults module and print its namespace */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100118 wdmod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
Radek Krejcie7b95092019-05-15 11:03:07 +0200119 if (wdmod && modlist_add(&mlist, wdmod)) {
120 goto print;
121 }
122 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100123
Radek Krejcie7b95092019-05-15 11:03:07 +0200124 LY_LIST_FOR(((struct lyd_node_inner*)node)->child, child) {
125 LYD_TREE_DFS_BEGIN(child, next, cur) {
126 for (attr = cur->attr; attr; attr = attr->next) {
127 if (!strcmp(cur->schema->name, "filter") &&
128 (!strcmp(cur->schema->module->name, "ietf-netconf") ||
129 !strcmp(cur->schema->module->name, "notifications"))) {
130 /* exception for NETCONF's filter attributes */
131 continue;
132 } else {
133 /* TODO annotations r = modlist_add(&mlist, lys_main_module(attr->annotation->module)); */
134 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200135 }
136 LYD_TREE_DFS_END(child, next, cur)}
137 }
138 break;
139 default:
140 break;
141 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200142
Radek Krejcie7b95092019-05-15 11:03:07 +0200143print:
144 /* print used namespaces */
145 while (mlist) {
146 miter = mlist;
147 mlist = mlist->next;
148
149 ly_print(ctx->out, " xmlns:%s=\"%s\"", miter->module->prefix, miter->module->ns);
150 free(miter);
151 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200152}
153
154/**
155 * @brief XML mapping of YANG modules to prefixes in values.
156 *
157 * Implementation of ly_clb_get_prefix
158 */
159static const char *
160xml_print_get_prefix(const struct lys_module *mod, void *private)
161{
162 struct ly_set *ns_list = (struct ly_set*)private;
163
164 ly_set_add(ns_list, (void*)mod, 0);
165 return mod->prefix;
Radek Krejcie7b95092019-05-15 11:03:07 +0200166}
167
168/**
169 * TODO
170 */
171static LY_ERR
172xml_print_attrs(struct xmlpr_ctx *ctx, const struct lyd_node *node)
173{
Radek Krejcie7b95092019-05-15 11:03:07 +0200174 struct lyd_attr *attr;
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100175 const struct lys_module *wdmod = NULL;
Radek Krejci28681fa2019-09-06 13:08:45 +0200176#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200177 const char **prefs, **nss;
178 const char *xml_expr = NULL, *mod_name;
179 uint32_t ns_count, i;
180 int rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200181 char *p;
182 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200183#endif
184 struct ly_set ns_list = {0};
185 int dynamic;
186 unsigned int u;
Radek Krejcie7b95092019-05-15 11:03:07 +0200187
Radek Krejcie7b95092019-05-15 11:03:07 +0200188 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100189 if (node->schema->nodetype & LYD_NODE_TERM) {
190 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG))) ||
191 ((ctx->options & LYDP_WD_ALL_TAG) && ly_is_default(node))) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200192 /* we have implicit OR explicit default node */
193 /* get with-defaults module */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100194 wdmod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
Radek Krejcie7b95092019-05-15 11:03:07 +0200195 if (wdmod) {
196 /* print attribute only if context include with-defaults schema */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100197 ly_print(ctx->out, " %s:default=\"true\"", wdmod->prefix);
Radek Krejcie7b95092019-05-15 11:03:07 +0200198 }
199 }
200 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100201#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200202 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
203 if (!strcmp(node->schema->name, "filter")
204 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
205 rpc_filter = 1;
206 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200207#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200208 for (attr = node->attr; attr; attr = attr->next) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200209 const char *value = attr->value.realtype->plugin->print(&attr->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
210
211 /* print namespaces connected with the values's prefixes */
212 for (u = 0; u < ns_list.count; ++u) {
213 const struct lys_module *mod = (const struct lys_module*)ns_list.objs[u];
214 ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
215 }
216 ly_set_erase(&ns_list, NULL);
217
218#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200219 if (rpc_filter) {
220 /* exception for NETCONF's filter's attributes */
221 if (!strcmp(attr->name, "select")) {
222 /* xpath content, we have to convert the JSON format into XML first */
223 xml_expr = transform_json2xml(node->schema->module, attr->value_str, 0, &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 ly_print(out, " %s=\"", attr->name);
236 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200237#endif
238 ly_print(ctx->out, " %s:%s=\"", attr->annotation->module->prefix, attr->name);
239#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200240 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200241#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200242
Radek Krejci28681fa2019-09-06 13:08:45 +0200243 if (value && value[0]) {
244 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200245 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200246 ly_print(ctx->out, "\"");
247 if (dynamic) {
248 free((void*)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200249 }
250 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200251
252 return LY_SUCCESS;
253}
254
255/**
256 * @brief Print generic XML element despite of the data node type.
257 *
258 * Prints the element name, attributes and necessary namespaces.
259 *
260 * @param[in] ctx XML printer context.
261 * @param[in] node Data node to be printed.
262 * @return LY_ERR value.
263 */
264static LY_ERR
265xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
266{
267 if (ctx->toplevel || !node->parent || node->schema->module != node->parent->schema->module) {
268 /* print "namespace" */
269 ly_print(ctx->out, "%*s<%s xmlns=\"%s\"", INDENT, node->schema->name, node->schema->module->ns);
270 } else {
271 ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
272 }
273
274 if (ctx->toplevel) {
275 xml_print_ns(ctx, node);
276 ctx->toplevel = 0;
277 }
278
279 LY_CHECK_RET(xml_print_attrs(ctx, node));
280
281 return LY_SUCCESS;
282}
283
284static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
285
286/**
287 * @brief Print XML element representing lyd_node_term.
288 *
289 * @param[in] ctx XML printer context.
290 * @param[in] node Data node to be printed.
291 * @return LY_ERR value.
292 */
293static LY_ERR
294xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
295{
Radek Krejcia1911222019-07-22 17:24:50 +0200296 struct ly_set ns_list = {0};
297 unsigned int u;
298 int dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200299 const char *value;
300
301 LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100302 value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(&node->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
Radek Krejcie7b95092019-05-15 11:03:07 +0200303
Radek Krejcia1911222019-07-22 17:24:50 +0200304 /* print namespaces connected with the values's prefixes */
305 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100306 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
307 ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200308 }
Radek Krejcia1911222019-07-22 17:24:50 +0200309 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200310
Radek Krejcia1911222019-07-22 17:24:50 +0200311 if (!value || !value[0]) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200312 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
313 } else {
314 ly_print(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200315 lyxml_dump_text(ctx->out, value, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200316 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
317 }
Radek Krejcia1911222019-07-22 17:24:50 +0200318 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100319 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200320 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200321
322 return LY_SUCCESS;
323}
324
325/**
326 * @brief Print XML element representing lyd_node_inner.
327 *
328 * @param[in] ctx XML printer context.
329 * @param[in] node Data node to be printed.
330 * @return LY_ERR value.
331 */
332static LY_ERR
333xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
334{
335 LY_ERR ret;
336 struct lyd_node *child;
337
338 LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
339
340 if (!node->child) {
341 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
342 return LY_SUCCESS;
343 }
344
345 /* children */
346 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
347
348 LEVEL_INC;
349 LY_LIST_FOR(node->child, child) {
350 ret = xml_print_node(ctx, child);
351 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
352 }
353 LEVEL_DEC;
354
355 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
356
357 return LY_SUCCESS;
358}
359
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200360static LY_ERR
361xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200362{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200363 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200364 struct lyd_node *iter;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200365 int options_backup;
Radek Krejcie7b95092019-05-15 11:03:07 +0200366
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200367 LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200368
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200369 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200370 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200371no_content:
372 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
373 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200374 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200375 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200376 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200377 /* close opening tag and print data */
378 options_backup = ctx->options;
379 ctx->options &= ~(LYDP_WITHSIBLINGS | LYDP_NETCONF);
380 LEVEL_INC;
381
382 ly_print(ctx->out, ">%s", LEVEL ? "\n" : "");
383 LY_LIST_FOR(any->value.tree, iter) {
384 if (xml_print_node(ctx, iter)) {
385 return EXIT_FAILURE;
Radek Krejcie7b95092019-05-15 11:03:07 +0200386 }
387 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200388
389 LEVEL_DEC;
390 ctx->options = options_backup;
Radek Krejcie7b95092019-05-15 11:03:07 +0200391 break;
392 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200393 /* escape XML-sensitive characters */
394 if (!any->value.str[0]) {
395 goto no_content;
396 }
397 /* close opening tag and print data */
398 ly_print(ctx->out, ">");
399 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200400 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200401 case LYD_ANYDATA_XML:
402 /* print without escaping special characters */
403 if (!any->value.str[0]) {
404 goto no_content;
405 }
406 ly_print(ctx->out, ">%s", any->value.str);
407 break;
408 case LYD_ANYDATA_JSON:
409#if 0 /* TODO LYB format */
410 case LYD_ANYDATA_LYB:
411#endif
412 /* JSON and LYB format is not supported */
413 LOGWRN(node->schema->module->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
414 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200415 }
416
417 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200418 if (any->value_type == LYD_ANYDATA_DATATREE) {
419 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
420 } else {
421 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
422 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200423 }
424
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200425 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200426}
Radek Krejcie7b95092019-05-15 11:03:07 +0200427
428/**
429 * @brief Print XML element representing lyd_node.
430 *
431 * @param[in] ctx XML printer context.
432 * @param[in] node Data node to be printed.
433 * @return LY_ERR value.
434 */
435static LY_ERR
436xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
437{
438 LY_ERR ret = LY_SUCCESS;
439
Michal Vasko9b368d32020-02-14 13:53:31 +0100440 if (!ly_should_print(node, ctx->options)) {
441 /* do not print at all */
Radek Krejcie7b95092019-05-15 11:03:07 +0200442 return EXIT_SUCCESS;
443 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200444
445 switch (node->schema->nodetype) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200446 case LYS_CONTAINER:
447 case LYS_LIST:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200448 case LYS_NOTIF:
449 case LYS_ACTION:
Radek Krejcie7b95092019-05-15 11:03:07 +0200450 ret = xml_print_inner(ctx, (const struct lyd_node_inner*)node);
451 break;
452 case LYS_LEAF:
453 case LYS_LEAFLIST:
454 ret = xml_print_term(ctx, (const struct lyd_node_term*)node);
455 break;
Radek Krejcie7b95092019-05-15 11:03:07 +0200456 case LYS_ANYXML:
457 case LYS_ANYDATA:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200458 ret = xml_print_anydata(ctx, (const struct lyd_node_any*)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200459 break;
Radek Krejcie7b95092019-05-15 11:03:07 +0200460 default:
461 LOGINT(node->schema->module->ctx);
462 ret = LY_EINT;
463 break;
464 }
465
466 return ret;
467}
468
469LY_ERR
470xml_print_data(struct lyout *out, const struct lyd_node *root, int options)
471{
472 const struct lyd_node *node;
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100473 struct xmlpr_ctx ctx_ = {.out = out, .level = (options & LYDP_FORMAT ? 1 : 0), .options = options}, *ctx = &ctx_;
Radek Krejcie7b95092019-05-15 11:03:07 +0200474
475 if (!root) {
476 if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
477 ly_print(out, "");
478 }
479 goto finish;
480 }
481
482 /* content */
483 LY_LIST_FOR(root, node) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100484 ctx_.toplevel = 1;
Radek Krejcie7b95092019-05-15 11:03:07 +0200485 if (xml_print_node(ctx, node)) {
486 return EXIT_FAILURE;
487 }
488 if (!(options & LYDP_WITHSIBLINGS)) {
489 break;
490 }
491 }
492
493finish:
494 ly_print_flush(out);
495 return LY_SUCCESS;
496}
497