blob: 71e53c073eadf6e80fbb58f2200abf1da584edcb [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
Radek Krejci535ea9f2020-05-29 16:01:05 +020016#include <assert.h>
17#include <stdint.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020018#include <stdlib.h>
19#include <string.h>
20
Radek Krejci535ea9f2020-05-29 16:01:05 +020021#include "common.h"
22#include "context.h"
23#include "dict.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include "log.h"
Radek Krejci7931b192020-06-25 17:05:03 +020025#include "parser_data.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020026#include "plugins_types.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020027#include "printer.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020028#include "printer_data.h"
29#include "printer_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020030#include "set.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020031#include "tree.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020032#include "tree_schema.h"
33#include "xml.h"
34
35/**
36 * @brief XML printer context.
37 */
38struct xmlpr_ctx {
Radek Krejci241f6b52020-05-21 18:13:49 +020039 struct ly_out *out; /**< output specification */
Radek Krejcie7b95092019-05-15 11:03:07 +020040 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
41 int options; /**< [Data printer flags](@ref dataprinterflags) */
Michal Vasko52927e22020-03-16 17:26:14 +010042 const struct ly_ctx *ctx; /**< libyang context */
43 struct ly_set prefix; /**< printed namespace prefixes */
44 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020045};
46
47#define LEVEL ctx->level /**< current level */
48#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
49#define LEVEL_INC if (LEVEL) {LEVEL++;} /**< increase indentation level */
50#define LEVEL_DEC if (LEVEL) {LEVEL--;} /**< decrease indentation level */
51
Michal Vasko52927e22020-03-16 17:26:14 +010052#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejcie7b95092019-05-15 11:03:07 +020053
54/**
Michal Vasko52927e22020-03-16 17:26:14 +010055 * @brief Print a namespace if not already printed.
56 *
57 * @param[in] ctx XML printer context.
58 * @param[in] ns Namespace to print, expected to be in dictionary.
59 * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
60 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
61 * @return Printed prefix of the namespace to use.
Radek Krejcie7b95092019-05-15 11:03:07 +020062 */
Michal Vasko52927e22020-03-16 17:26:14 +010063static const char *
64xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, int prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020065{
Michal Vasko52927e22020-03-16 17:26:14 +010066 int i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010067
Michal Vasko52927e22020-03-16 17:26:14 +010068 for (i = ctx->ns.count - 1; i > -1; --i) {
69 if (!new_prefix) {
70 /* find default namespace */
71 if (!ctx->prefix.objs[i]) {
72 if (ctx->ns.objs[i] != ns) {
73 /* different default namespace */
74 i = -1;
Radek Krejcie7b95092019-05-15 11:03:07 +020075 }
Michal Vasko52927e22020-03-16 17:26:14 +010076 break;
77 }
78 } else {
79 /* find prefixed namespace */
80 if (ctx->ns.objs[i] == ns) {
81 if (!ctx->prefix.objs[i]) {
82 /* default namespace is not interesting */
83 continue;
84 }
85
86 if (!strcmp(ctx->prefix.objs[i], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
87 /* the same prefix or can be any */
88 break;
89 }
90 }
Radek Krejcie7b95092019-05-15 11:03:07 +020091 }
Radek Krejcie7b95092019-05-15 11:03:07 +020092 }
Radek Krejci28681fa2019-09-06 13:08:45 +020093
Michal Vasko52927e22020-03-16 17:26:14 +010094 if (i == -1) {
95 /* suitable namespace not found, must be printed */
Radek Krejci241f6b52020-05-21 18:13:49 +020096 ly_print(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020097
Michal Vasko52927e22020-03-16 17:26:14 +010098 /* and added into namespaces */
99 if (new_prefix) {
100 new_prefix = lydict_insert(ctx->ctx, new_prefix, 0);
101 }
102 ly_set_add(&ctx->prefix, (void *)new_prefix, LY_SET_OPT_USEASLIST);
103 i = ly_set_add(&ctx->ns, (void *)ns, LY_SET_OPT_USEASLIST);
Radek Krejcie7b95092019-05-15 11:03:07 +0200104 }
Michal Vasko52927e22020-03-16 17:26:14 +0100105
106 /* return it */
107 return ctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200108}
109
110/**
111 * @brief XML mapping of YANG modules to prefixes in values.
112 *
113 * Implementation of ly_clb_get_prefix
114 */
115static const char *
116xml_print_get_prefix(const struct lys_module *mod, void *private)
117{
118 struct ly_set *ns_list = (struct ly_set*)private;
119
120 ly_set_add(ns_list, (void*)mod, 0);
121 return mod->prefix;
Radek Krejcie7b95092019-05-15 11:03:07 +0200122}
123
124/**
125 * TODO
126 */
Michal Vasko52927e22020-03-16 17:26:14 +0100127static void
Michal Vasko9f96a052020-03-10 09:41:45 +0100128xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200129{
Michal Vasko9f96a052020-03-10 09:41:45 +0100130 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100131 const struct lys_module *mod;
132 struct ly_set ns_list = {0};
Radek Krejci28681fa2019-09-06 13:08:45 +0200133#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200134 const char **prefs, **nss;
135 const char *xml_expr = NULL, *mod_name;
136 uint32_t ns_count, i;
137 int rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200138 char *p;
139 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200140#endif
Radek Krejci28681fa2019-09-06 13:08:45 +0200141 int dynamic;
142 unsigned int u;
Radek Krejcie7b95092019-05-15 11:03:07 +0200143
Radek Krejcie7b95092019-05-15 11:03:07 +0200144 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100145 if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci7931b192020-06-25 17:05:03 +0200146 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
147 ((ctx->options & LYD_PRINT_WD_ALL_TAG) && ly_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100148 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
149 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
150 if (mod) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200151 ly_print(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200152 }
153 }
154 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100155#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200156 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
157 if (!strcmp(node->schema->name, "filter")
158 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
159 rpc_filter = 1;
160 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200161#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100162 for (meta = node->meta; meta; meta = meta->next) {
163 const char *value = meta->value.realtype->plugin->print(&meta->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
Radek Krejci28681fa2019-09-06 13:08:45 +0200164
Michal Vasko52927e22020-03-16 17:26:14 +0100165 /* print namespaces connected with the value's prefixes */
Radek Krejci28681fa2019-09-06 13:08:45 +0200166 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100167 mod = (const struct lys_module *)ns_list.objs[u];
168 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200169 }
170 ly_set_erase(&ns_list, NULL);
171
172#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200173 if (rpc_filter) {
174 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100175 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200176 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100177 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200178 if (!xml_expr) {
179 /* error */
180 return EXIT_FAILURE;
181 }
182
183 for (i = 0; i < ns_count; ++i) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200184 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
Radek Krejcie7b95092019-05-15 11:03:07 +0200185 }
186 free(prefs);
187 free(nss);
188 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200189 ly_print(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200190 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200191#endif
Michal Vasko52927e22020-03-16 17:26:14 +0100192 /* print the metadata with its namespace */
193 mod = meta->annotation->module;
Radek Krejci241f6b52020-05-21 18:13:49 +0200194 ly_print(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200195#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200196 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200197#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200198
Michal Vasko52927e22020-03-16 17:26:14 +0100199 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200200 if (value && value[0]) {
201 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200202 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200203 ly_print(ctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200204 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100205 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200206 }
207 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200208}
209
210/**
211 * @brief Print generic XML element despite of the data node type.
212 *
213 * Prints the element name, attributes and necessary namespaces.
214 *
215 * @param[in] ctx XML printer context.
216 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200217 */
Michal Vasko52927e22020-03-16 17:26:14 +0100218static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200219xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
220{
Michal Vasko52927e22020-03-16 17:26:14 +0100221 /* print node name */
Radek Krejci241f6b52020-05-21 18:13:49 +0200222 ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100223
224 /* print default namespace */
225 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
226
227 /* print metadata */
228 xml_print_meta(ctx, node);
229}
230
231static LY_ERR
232xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
233{
234 const struct ly_attr *attr;
235 const char *pref;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200236 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100237
238 LY_LIST_FOR(node->attr, attr) {
239 pref = NULL;
240 if (attr->prefix.pref) {
241 /* print attribute namespace */
242 switch (attr->format) {
243 case LYD_XML:
244 pref = xml_print_ns(ctx, attr->prefix.ns, attr->prefix.pref, 0);
245 break;
246 case LYD_SCHEMA:
Michal Vasko60ea6352020-06-29 13:39:39 +0200247 case LYD_LYB:
Michal Vasko52927e22020-03-16 17:26:14 +0100248 /* cannot be created */
249 LOGINT(node->ctx);
250 return LY_EINT;
251 }
252 }
253
254 /* print namespaces connected with the value's prefixes */
255 if (attr->val_prefs) {
256 LY_ARRAY_FOR(attr->val_prefs, u) {
257 xml_print_ns(ctx, attr->val_prefs[u].ns, attr->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
258 }
259 }
260
261 /* print the attribute with its prefix and value */
Radek Krejci241f6b52020-05-21 18:13:49 +0200262 ly_print(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200263 }
264
Michal Vasko52927e22020-03-16 17:26:14 +0100265 return LY_SUCCESS;
266}
267
268static LY_ERR
269xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
270{
271 /* print node name */
Radek Krejci241f6b52020-05-21 18:13:49 +0200272 ly_print(ctx->out, "%*s<%s", INDENT, node->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100273
274 /* print default namespace */
275 switch (node->format) {
276 case LYD_XML:
277 xml_print_ns(ctx, node->prefix.ns, NULL, 0);
278 break;
279 case LYD_SCHEMA:
Michal Vasko60ea6352020-06-29 13:39:39 +0200280 case LYD_LYB:
Michal Vasko52927e22020-03-16 17:26:14 +0100281 /* cannot be created */
282 LOGINT(node->ctx);
283 return LY_EINT;
Radek Krejcie7b95092019-05-15 11:03:07 +0200284 }
285
Michal Vasko52927e22020-03-16 17:26:14 +0100286 /* print attributes */
287 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200288
289 return LY_SUCCESS;
290}
291
292static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
293
294/**
295 * @brief Print XML element representing lyd_node_term.
296 *
297 * @param[in] ctx XML printer context.
298 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200299 */
Michal Vasko52927e22020-03-16 17:26:14 +0100300static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200301xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
302{
Radek Krejcia1911222019-07-22 17:24:50 +0200303 struct ly_set ns_list = {0};
304 unsigned int u;
305 int dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200306 const char *value;
307
Michal Vasko52927e22020-03-16 17:26:14 +0100308 xml_print_node_open(ctx, (struct lyd_node *)node);
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100309 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 +0200310
Radek Krejcia1911222019-07-22 17:24:50 +0200311 /* print namespaces connected with the values's prefixes */
312 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100313 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Radek Krejci241f6b52020-05-21 18:13:49 +0200314 ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200315 }
Radek Krejcia1911222019-07-22 17:24:50 +0200316 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200317
Radek Krejcia1911222019-07-22 17:24:50 +0200318 if (!value || !value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200319 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200320 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200321 ly_print(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200322 lyxml_dump_text(ctx->out, value, 0);
Radek Krejci241f6b52020-05-21 18:13:49 +0200323 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200324 }
Radek Krejcia1911222019-07-22 17:24:50 +0200325 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100326 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200327 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200328}
329
330/**
331 * @brief Print XML element representing lyd_node_inner.
332 *
333 * @param[in] ctx XML printer context.
334 * @param[in] node Data node to be printed.
335 * @return LY_ERR value.
336 */
337static LY_ERR
338xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
339{
340 LY_ERR ret;
341 struct lyd_node *child;
342
Michal Vasko52927e22020-03-16 17:26:14 +0100343 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200344
345 if (!node->child) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200346 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200347 return LY_SUCCESS;
348 }
349
350 /* children */
Radek Krejci241f6b52020-05-21 18:13:49 +0200351 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200352
353 LEVEL_INC;
354 LY_LIST_FOR(node->child, child) {
355 ret = xml_print_node(ctx, child);
356 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
357 }
358 LEVEL_DEC;
359
Radek Krejci241f6b52020-05-21 18:13:49 +0200360 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200361
362 return LY_SUCCESS;
363}
364
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200365static LY_ERR
366xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200367{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200368 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200369 struct lyd_node *iter;
Michal Vasko60ea6352020-06-29 13:39:39 +0200370 int prev_opts, prev_lo;
Michal Vasko52927e22020-03-16 17:26:14 +0100371 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200372
Michal Vasko52927e22020-03-16 17:26:14 +0100373 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200374
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200375 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200376 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200377no_content:
Radek Krejci241f6b52020-05-21 18:13:49 +0200378 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200379 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200380 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200381 if (any->value_type == LYD_ANYDATA_LYB) {
382 /* turn logging off */
383 prev_lo = ly_log_options(0);
384
385 /* try to parse it into a data tree */
Radek Krejci7931b192020-06-25 17:05:03 +0200386 if (lyd_parse_data_mem((struct ly_ctx *)LYD_NODE_CTX(node), any->value.mem, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0, &iter) == LY_SUCCESS) {
Michal Vasko60ea6352020-06-29 13:39:39 +0200387 /* successfully parsed */
388 free(any->value.mem);
389 any->value.tree = iter;
390 any->value_type = LYD_ANYDATA_DATATREE;
391 }
Radek Krejci7931b192020-06-25 17:05:03 +0200392
393 /* turn loggin on again */
394 ly_log_options(prev_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200395 }
396
Radek Krejcie7b95092019-05-15 11:03:07 +0200397 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200398 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200399 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100400 prev_opts = ctx->options;
Radek Krejci7931b192020-06-25 17:05:03 +0200401 ctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200402 LEVEL_INC;
403
Radek Krejci241f6b52020-05-21 18:13:49 +0200404 ly_print(ctx->out, ">%s", LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200405 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100406 ret = xml_print_node(ctx, iter);
407 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200408 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200409
410 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100411 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200412 break;
413 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200414 /* escape XML-sensitive characters */
415 if (!any->value.str[0]) {
416 goto no_content;
417 }
418 /* close opening tag and print data */
Radek Krejci241f6b52020-05-21 18:13:49 +0200419 ly_print(ctx->out, ">");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200420 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200421 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200422 case LYD_ANYDATA_XML:
423 /* print without escaping special characters */
424 if (!any->value.str[0]) {
425 goto no_content;
426 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200427 ly_print(ctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200428 break;
429 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200430 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200431 /* JSON and LYB format is not supported */
Michal Vasko60ea6352020-06-29 13:39:39 +0200432 LOGWRN(LYD_NODE_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200433 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200434 }
435
436 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200437 if (any->value_type == LYD_ANYDATA_DATATREE) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200438 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200439 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200440 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200441 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200442 }
443
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200444 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200445}
Radek Krejcie7b95092019-05-15 11:03:07 +0200446
Michal Vasko52927e22020-03-16 17:26:14 +0100447static LY_ERR
448xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
449{
450 LY_ERR ret;
451 struct lyd_node *child;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200452 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100453
454 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
455
456 if (node->value[0]) {
457 /* print namespaces connected with the value's prefixes */
458 if (node->val_prefs) {
459 LY_ARRAY_FOR(node->val_prefs, u) {
460 xml_print_ns(ctx, node->val_prefs[u].ns, node->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
461 }
462 }
463
Radek Krejci241f6b52020-05-21 18:13:49 +0200464 ly_print(ctx->out, ">%s", node->value);
Michal Vasko52927e22020-03-16 17:26:14 +0100465 }
466
467 if (node->child) {
468 /* children */
469 if (!node->value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200470 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100471 }
472
473 LEVEL_INC;
474 LY_LIST_FOR(node->child, child) {
475 ret = xml_print_node(ctx, child);
476 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
477 }
478 LEVEL_DEC;
479
Radek Krejci241f6b52020-05-21 18:13:49 +0200480 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->name, LEVEL ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100481 } else if (node->value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200482 ly_print(ctx->out, "</%s>%s", node->name, LEVEL ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100483 } else {
484 /* no value or children */
Radek Krejci241f6b52020-05-21 18:13:49 +0200485 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100486 }
487
488 return LY_SUCCESS;
489}
490
Radek Krejcie7b95092019-05-15 11:03:07 +0200491/**
492 * @brief Print XML element representing lyd_node.
493 *
494 * @param[in] ctx XML printer context.
495 * @param[in] node Data node to be printed.
496 * @return LY_ERR value.
497 */
498static LY_ERR
499xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
500{
501 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100502 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200503
Michal Vasko9b368d32020-02-14 13:53:31 +0100504 if (!ly_should_print(node, ctx->options)) {
505 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100506 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200507 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200508
Michal Vasko52927e22020-03-16 17:26:14 +0100509 /* remember namespace definition count on this level */
510 ns_count = ctx->ns.count;
511
512 if (!node->schema) {
513 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
514 } else {
515 switch (node->schema->nodetype) {
516 case LYS_CONTAINER:
517 case LYS_LIST:
518 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100519 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100520 case LYS_ACTION:
521 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
522 break;
523 case LYS_LEAF:
524 case LYS_LEAFLIST:
525 xml_print_term(ctx, (const struct lyd_node_term *)node);
526 break;
527 case LYS_ANYXML:
528 case LYS_ANYDATA:
529 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
530 break;
531 default:
532 LOGINT(node->schema->module->ctx);
533 ret = LY_EINT;
534 break;
535 }
536 }
537
538 /* remove all added namespaces */
539 while (ns_count < ctx->ns.count) {
540 FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
541 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
542 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200543 }
544
545 return ret;
546}
547
548LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200549xml_print_data(struct ly_out *out, const struct lyd_node *root, int options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200550{
551 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100552 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200553
554 if (!root) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200555 if (out->type == LY_OUT_MEMORY || out->type == LY_OUT_CALLBACK) {
556 ly_print(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200557 }
558 goto finish;
559 }
560
Michal Vasko52927e22020-03-16 17:26:14 +0100561 ctx.out = out;
Radek Krejci7931b192020-06-25 17:05:03 +0200562 ctx.level = (options & LYD_PRINT_FORMAT ? 1 : 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100563 ctx.options = options;
564 ctx.ctx = LYD_NODE_CTX(root);
565
Radek Krejcie7b95092019-05-15 11:03:07 +0200566 /* content */
567 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100568 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejci7931b192020-06-25 17:05:03 +0200569 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200570 break;
571 }
572 }
573
574finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100575 assert(!ctx.prefix.count && !ctx.ns.count);
576 ly_set_erase(&ctx.prefix, NULL);
577 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200578 ly_print_flush(out);
579 return LY_SUCCESS;
580}
581