Coverage Report

Created: 2025-12-12 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/src/printer_xml.c
Line
Count
Source
1
/**
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 <assert.h>
17
#include <stdint.h>
18
#include <stdlib.h>
19
#include <string.h>
20
21
#include "common.h"
22
#include "context.h"
23
#include "dict.h"
24
#include "log.h"
25
#include "out.h"
26
#include "out_internal.h"
27
#include "parser_data.h"
28
#include "plugins_types.h"
29
#include "printer_data.h"
30
#include "printer_internal.h"
31
#include "set.h"
32
#include "tree.h"
33
#include "tree_data.h"
34
#include "tree_schema.h"
35
#include "xml.h"
36
37
/**
38
 * @brief XML printer context.
39
 */
40
struct xmlpr_ctx {
41
    struct ly_out *out;       /**< output specification */
42
    uint16_t level;           /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
43
    uint32_t options;         /**< [Data printer flags](@ref dataprinterflags) */
44
    const struct ly_ctx *ctx; /**< libyang context */
45
    struct ly_set prefix;     /**< printed namespace prefixes */
46
    struct ly_set ns;         /**< printed namespaces */
47
};
48
49
0
#define LYXML_PREFIX_REQUIRED 0x01  /**< The prefix is not just a suggestion but a requirement. */
50
0
#define LYXML_PREFIX_DEFAULT  0x02  /**< The namespace is required to be a default (without prefix) */
51
52
/**
53
 * @brief Print a namespace if not already printed.
54
 *
55
 * @param[in] ctx XML printer context.
56
 * @param[in] ns Namespace to print, expected to be in dictionary.
57
 * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
58
 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
59
 * @return Printed prefix of the namespace to use.
60
 */
61
static const char *
62
xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, uint32_t prefix_opts)
63
0
{
64
0
    uint32_t i;
65
66
0
    for (i = ctx->ns.count; i > 0; --i) {
67
0
        if (!new_prefix) {
68
            /* find default namespace */
69
0
            if (!ctx->prefix.objs[i - 1]) {
70
0
                if (!strcmp(ctx->ns.objs[i - 1], ns)) {
71
                    /* matching default namespace */
72
0
                    return ctx->prefix.objs[i - 1];
73
0
                }
74
                /* not matching default namespace */
75
0
                break;
76
0
            }
77
0
        } else {
78
            /* find prefixed namespace */
79
0
            if (!strcmp(ctx->ns.objs[i - 1], ns)) {
80
0
                if (!ctx->prefix.objs[i - 1]) {
81
                    /* default namespace is not interesting */
82
0
                    continue;
83
0
                }
84
85
0
                if (!strcmp(ctx->prefix.objs[i - 1], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
86
                    /* the same prefix or can be any */
87
0
                    return ctx->prefix.objs[i - 1];
88
0
                }
89
0
            }
90
0
        }
91
0
    }
92
93
    /* suitable namespace not found, must be printed */
94
0
    ly_print_(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
95
96
    /* and added into namespaces */
97
0
    if (new_prefix) {
98
0
        LY_CHECK_RET(lydict_insert(ctx->ctx, new_prefix, 0, &new_prefix), NULL);
99
0
    }
100
0
    LY_CHECK_RET(ly_set_add(&ctx->prefix, (void *)new_prefix, 1, NULL), NULL);
101
0
    LY_CHECK_RET(ly_set_add(&ctx->ns, (void *)ns, 1, &i), NULL);
102
103
    /* return it */
104
0
    return ctx->prefix.objs[i];
105
0
}
106
107
static const char *
108
xml_print_ns_opaq(struct xmlpr_ctx *ctx, LY_VALUE_FORMAT format, const struct ly_opaq_name *name, uint32_t prefix_opts)
109
0
{
110
0
    switch (format) {
111
0
    case LY_VALUE_XML:
112
0
        return xml_print_ns(ctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
113
0
        break;
114
0
    case LY_VALUE_JSON:
115
0
        if (name->module_name) {
116
0
            const struct lys_module *mod = ly_ctx_get_module_latest(ctx->ctx, name->module_name);
117
0
            if (mod) {
118
0
                return xml_print_ns(ctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
119
0
            }
120
0
        }
121
0
        break;
122
0
    default:
123
        /* cannot be created */
124
0
        LOGINT(ctx->ctx);
125
0
    }
126
127
0
    return NULL;
128
0
}
129
130
/**
131
 * @brief Print prefix data.
132
 *
133
 * @param[in] ctx XML printer context.
134
 * @param[in] format Value prefix format, only ::LY_VALUE_XML supported.
135
 * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
136
 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
137
 * @return LY_ERR value.
138
 */
139
static void
140
xml_print_ns_prefix_data(struct xmlpr_ctx *ctx, LY_VALUE_FORMAT format, void *prefix_data, uint32_t prefix_opts)
141
0
{
142
0
    const struct ly_set *set;
143
0
    const struct lyxml_ns *ns;
144
0
    uint32_t i;
145
146
0
    switch (format) {
147
0
    case LY_VALUE_XML:
148
0
        set = prefix_data;
149
0
        for (i = 0; i < set->count; ++i) {
150
0
            ns = set->objs[i];
151
0
            xml_print_ns(ctx, ns->uri, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : ns->prefix, prefix_opts);
152
0
        }
153
0
        break;
154
0
    default:
155
        /* cannot be created */
156
0
        LOGINT(ctx->ctx);
157
0
    }
158
0
}
159
160
/**
161
 * TODO
162
 */
163
static void
164
xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
165
0
{
166
0
    struct lyd_meta *meta;
167
0
    const struct lys_module *mod;
168
0
    struct ly_set ns_list = {0};
169
170
#if 0
171
    const char **prefs, **nss;
172
    const char *xml_expr = NULL, *mod_name;
173
    uint32_t ns_count, i;
174
    ly_bool rpc_filter = 0;
175
    char *p;
176
    size_t len;
177
#endif
178
0
    ly_bool dynamic;
179
180
    /* with-defaults */
181
0
    if (node->schema->nodetype & LYD_NODE_TERM) {
182
0
        if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
183
0
                ((ctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
184
            /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
185
0
            mod = ly_ctx_get_module_latest(LYD_CTX(node), "ietf-netconf-with-defaults");
186
0
            if (mod) {
187
0
                ly_print_(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
188
0
            }
189
0
        }
190
0
    }
191
#if 0
192
    /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
193
    if (!strcmp(node->schema->name, "filter") &&
194
            (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
195
        rpc_filter = 1;
196
    }
197
#endif
198
0
    for (meta = node->meta; meta; meta = meta->next) {
199
0
        const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list,
200
0
                &dynamic, NULL);
201
202
        /* print namespaces connected with the value's prefixes */
203
0
        for (uint32_t u = 0; u < ns_list.count; ++u) {
204
0
            mod = (const struct lys_module *)ns_list.objs[u];
205
0
            xml_print_ns(ctx, mod->ns, mod->prefix, 1);
206
0
        }
207
0
        ly_set_erase(&ns_list, NULL);
208
209
#if 0
210
        if (rpc_filter) {
211
            /* exception for NETCONF's filter's attributes */
212
            if (!strcmp(meta->name, "select")) {
213
                /* xpath content, we have to convert the JSON format into XML first */
214
                xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
215
                if (!xml_expr) {
216
                    /* error */
217
                    return EXIT_FAILURE;
218
                }
219
220
                for (i = 0; i < ns_count; ++i) {
221
                    ly_print_(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
222
                }
223
                free(prefs);
224
                free(nss);
225
            }
226
            ly_print_(out, " %s=\"", meta->name);
227
        } else {
228
#endif
229
        /* print the metadata with its namespace */
230
0
        mod = meta->annotation->module;
231
0
        ly_print_(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
232
#if 0
233
    }
234
#endif
235
236
        /* print metadata value */
237
0
        if (value && value[0]) {
238
0
            lyxml_dump_text(ctx->out, value, 1);
239
0
        }
240
0
        ly_print_(ctx->out, "\"");
241
0
        if (dynamic) {
242
0
            free((void *)value);
243
0
        }
244
0
    }
245
0
}
246
247
/**
248
 * @brief Print generic XML element despite of the data node type.
249
 *
250
 * Prints the element name, attributes and necessary namespaces.
251
 *
252
 * @param[in] ctx XML printer context.
253
 * @param[in] node Data node to be printed.
254
 */
255
static void
256
xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
257
0
{
258
    /* print node name */
259
0
    ly_print_(ctx->out, "%*s<%s", INDENT, node->schema->name);
260
261
    /* print default namespace */
262
0
    xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
263
264
    /* print metadata */
265
0
    xml_print_meta(ctx, node);
266
0
}
267
268
static LY_ERR
269
xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
270
0
{
271
0
    const struct lyd_attr *attr;
272
0
    const char *pref;
273
274
0
    LY_LIST_FOR(node->attr, attr) {
275
0
        pref = NULL;
276
0
        if (attr->name.prefix) {
277
            /* print attribute namespace */
278
0
            pref = xml_print_ns_opaq(ctx, attr->format, &attr->name, 0);
279
0
        }
280
281
        /* print namespaces connected with the value's prefixes */
282
0
        if (attr->val_prefix_data) {
283
0
            xml_print_ns_prefix_data(ctx, attr->format, attr->val_prefix_data, LYXML_PREFIX_REQUIRED);
284
0
        }
285
286
        /* print the attribute with its prefix and value */
287
0
        ly_print_(ctx->out, " %s%s%s=\"", pref ? pref : "", pref ? ":" : "", attr->name.name);
288
0
        lyxml_dump_text(ctx->out, attr->value, 1);
289
0
        ly_print_(ctx->out, "\""); /* print attribute value terminator */
290
291
0
    }
292
293
0
    return LY_SUCCESS;
294
0
}
295
296
static LY_ERR
297
xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
298
0
{
299
    /* print node name */
300
0
    ly_print_(ctx->out, "%*s<%s", INDENT, node->name.name);
301
302
    /* print default namespace */
303
0
    xml_print_ns_opaq(ctx, node->format, &node->name, LYXML_PREFIX_DEFAULT);
304
305
    /* print attributes */
306
0
    LY_CHECK_RET(xml_print_attr(ctx, node));
307
308
0
    return LY_SUCCESS;
309
0
}
310
311
static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
312
313
/**
314
 * @brief Print XML element representing lyd_node_term.
315
 *
316
 * @param[in] ctx XML printer context.
317
 * @param[in] node Data node to be printed.
318
 */
319
static void
320
xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
321
0
{
322
0
    struct ly_set ns_list = {0};
323
0
    ly_bool dynamic;
324
0
    const char *value;
325
326
0
    xml_print_node_open(ctx, &node->node);
327
0
    value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML,
328
0
            &ns_list, &dynamic, NULL);
329
330
    /* print namespaces connected with the values's prefixes */
331
0
    for (uint32_t u = 0; u < ns_list.count; ++u) {
332
0
        const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
333
0
        ly_print_(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
334
0
    }
335
0
    ly_set_erase(&ns_list, NULL);
336
337
0
    if (!value || !value[0]) {
338
0
        ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
339
0
    } else {
340
0
        ly_print_(ctx->out, ">");
341
0
        lyxml_dump_text(ctx->out, value, 0);
342
0
        ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
343
0
    }
344
0
    if (dynamic) {
345
0
        free((void *)value);
346
0
    }
347
0
}
348
349
/**
350
 * @brief Print XML element representing lyd_node_inner.
351
 *
352
 * @param[in] ctx XML printer context.
353
 * @param[in] node Data node to be printed.
354
 * @return LY_ERR value.
355
 */
356
static LY_ERR
357
xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
358
0
{
359
0
    LY_ERR ret;
360
0
    struct lyd_node *child;
361
362
0
    xml_print_node_open(ctx, &node->node);
363
364
0
    LY_LIST_FOR(node->child, child) {
365
0
        if (ly_should_print(child, ctx->options)) {
366
0
            break;
367
0
        }
368
0
    }
369
0
    if (!child) {
370
        /* there are no children that will be printed */
371
0
        ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
372
0
        return LY_SUCCESS;
373
0
    }
374
375
    /* children */
376
0
    ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
377
378
0
    LEVEL_INC;
379
0
    LY_LIST_FOR(node->child, child) {
380
0
        ret = xml_print_node(ctx, child);
381
0
        LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
382
0
    }
383
0
    LEVEL_DEC;
384
385
0
    ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
386
387
0
    return LY_SUCCESS;
388
0
}
389
390
static LY_ERR
391
xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
392
0
{
393
0
    struct lyd_node_any *any = (struct lyd_node_any *)node;
394
0
    struct lyd_node *iter;
395
0
    uint32_t prev_opts, prev_lo;
396
0
    LY_ERR ret;
397
398
0
    xml_print_node_open(ctx, &node->node);
399
400
0
    if (!any->value.tree) {
401
        /* no content */
402
0
no_content:
403
0
        ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
404
0
        return LY_SUCCESS;
405
0
    } else {
406
0
        if (any->value_type == LYD_ANYDATA_LYB) {
407
            /* turn logging off */
408
0
            prev_lo = ly_log_options(0);
409
410
            /* try to parse it into a data tree */
411
0
            if (lyd_parse_data_mem((struct ly_ctx *)LYD_CTX(node), any->value.mem, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0, &iter) == LY_SUCCESS) {
412
                /* successfully parsed */
413
0
                free(any->value.mem);
414
0
                any->value.tree = iter;
415
0
                any->value_type = LYD_ANYDATA_DATATREE;
416
0
            }
417
418
            /* turn loggin on again */
419
0
            ly_log_options(prev_lo);
420
0
        }
421
422
0
        switch (any->value_type) {
423
0
        case LYD_ANYDATA_DATATREE:
424
            /* close opening tag and print data */
425
0
            prev_opts = ctx->options;
426
0
            ctx->options &= ~LYD_PRINT_WITHSIBLINGS;
427
0
            LEVEL_INC;
428
429
0
            ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
430
0
            LY_LIST_FOR(any->value.tree, iter) {
431
0
                ret = xml_print_node(ctx, iter);
432
0
                LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
433
0
            }
434
435
0
            LEVEL_DEC;
436
0
            ctx->options = prev_opts;
437
0
            break;
438
0
        case LYD_ANYDATA_STRING:
439
            /* escape XML-sensitive characters */
440
0
            if (!any->value.str[0]) {
441
0
                goto no_content;
442
0
            }
443
            /* close opening tag and print data */
444
0
            ly_print_(ctx->out, ">");
445
0
            lyxml_dump_text(ctx->out, any->value.str, 0);
446
0
            break;
447
0
        case LYD_ANYDATA_XML:
448
            /* print without escaping special characters */
449
0
            if (!any->value.str[0]) {
450
0
                goto no_content;
451
0
            }
452
0
            ly_print_(ctx->out, ">%s", any->value.str);
453
0
            break;
454
0
        case LYD_ANYDATA_JSON:
455
0
        case LYD_ANYDATA_LYB:
456
            /* JSON and LYB format is not supported */
457
0
            LOGWRN(LYD_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
458
0
            goto no_content;
459
0
        }
460
461
        /* closing tag */
462
0
        if (any->value_type == LYD_ANYDATA_DATATREE) {
463
0
            ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
464
0
        } else {
465
0
            ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
466
0
        }
467
0
    }
468
469
0
    return LY_SUCCESS;
470
0
}
471
472
static LY_ERR
473
xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
474
0
{
475
0
    LY_ERR ret;
476
0
    struct lyd_node *child;
477
478
0
    LY_CHECK_RET(xml_print_opaq_open(ctx, node));
479
480
0
    if (node->value[0]) {
481
        /* print namespaces connected with the value's prefixes */
482
0
        if (node->val_prefix_data) {
483
0
            xml_print_ns_prefix_data(ctx, node->format, node->val_prefix_data, LYXML_PREFIX_REQUIRED);
484
0
        }
485
486
0
        ly_print_(ctx->out, ">");
487
0
        lyxml_dump_text(ctx->out, node->value, 0);
488
0
    }
489
490
0
    if (node->child) {
491
        /* children */
492
0
        if (!node->value[0]) {
493
0
            ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
494
0
        }
495
496
0
        LEVEL_INC;
497
0
        LY_LIST_FOR(node->child, child) {
498
0
            ret = xml_print_node(ctx, child);
499
0
            LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
500
0
        }
501
0
        LEVEL_DEC;
502
503
0
        ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->name.name, DO_FORMAT ? "\n" : "");
504
0
    } else if (node->value[0]) {
505
0
        ly_print_(ctx->out, "</%s>%s", node->name.name, DO_FORMAT ? "\n" : "");
506
0
    } else {
507
        /* no value or children */
508
0
        ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
509
0
    }
510
511
0
    return LY_SUCCESS;
512
0
}
513
514
/**
515
 * @brief Print XML element representing lyd_node.
516
 *
517
 * @param[in] ctx XML printer context.
518
 * @param[in] node Data node to be printed.
519
 * @return LY_ERR value.
520
 */
521
static LY_ERR
522
xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
523
0
{
524
0
    LY_ERR ret = LY_SUCCESS;
525
0
    uint32_t ns_count;
526
527
0
    if (!ly_should_print(node, ctx->options)) {
528
        /* do not print at all */
529
0
        return LY_SUCCESS;
530
0
    }
531
532
    /* remember namespace definition count on this level */
533
0
    ns_count = ctx->ns.count;
534
535
0
    if (!node->schema) {
536
0
        ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
537
0
    } else {
538
0
        switch (node->schema->nodetype) {
539
0
        case LYS_CONTAINER:
540
0
        case LYS_LIST:
541
0
        case LYS_NOTIF:
542
0
        case LYS_RPC:
543
0
        case LYS_ACTION:
544
0
            ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
545
0
            break;
546
0
        case LYS_LEAF:
547
0
        case LYS_LEAFLIST:
548
0
            xml_print_term(ctx, (const struct lyd_node_term *)node);
549
0
            break;
550
0
        case LYS_ANYXML:
551
0
        case LYS_ANYDATA:
552
0
            ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
553
0
            break;
554
0
        default:
555
0
            LOGINT(node->schema->module->ctx);
556
0
            ret = LY_EINT;
557
0
            break;
558
0
        }
559
0
    }
560
561
    /* remove all added namespaces */
562
0
    while (ns_count < ctx->ns.count) {
563
0
        lydict_remove(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
564
0
        ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
565
0
        ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
566
0
    }
567
568
0
    return ret;
569
0
}
570
571
LY_ERR
572
xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
573
0
{
574
0
    const struct lyd_node *node;
575
0
    struct xmlpr_ctx ctx = {0};
576
577
0
    if (!root) {
578
0
        if ((out->type == LY_OUT_MEMORY) || (out->type == LY_OUT_CALLBACK)) {
579
0
            ly_print_(out, "");
580
0
        }
581
0
        goto finish;
582
0
    }
583
584
0
    ctx.out = out;
585
0
    ctx.level = 0;
586
0
    ctx.options = options;
587
0
    ctx.ctx = LYD_CTX(root);
588
589
    /* content */
590
0
    LY_LIST_FOR(root, node) {
591
0
        LY_CHECK_RET(xml_print_node(&ctx, node));
592
0
        if (!(options & LYD_PRINT_WITHSIBLINGS)) {
593
0
            break;
594
0
        }
595
0
    }
596
597
0
finish:
598
0
    assert(!ctx.prefix.count && !ctx.ns.count);
599
0
    ly_set_erase(&ctx.prefix, NULL);
600
    ly_set_erase(&ctx.ns, NULL);
601
0
    ly_print_flush(out);
602
0
    return LY_SUCCESS;
603
0
}