blob: d143ba7cccb59b832910b379f71d734a225a551c [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"
25#include "plugins_types.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020026#include "printer.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020027#include "printer_data.h"
28#include "printer_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "set.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020030#include "tree.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020031#include "tree_schema.h"
32#include "xml.h"
33
34/**
35 * @brief XML printer context.
36 */
37struct xmlpr_ctx {
Radek Krejci241f6b52020-05-21 18:13:49 +020038 struct ly_out *out; /**< output specification */
Radek Krejcie7b95092019-05-15 11:03:07 +020039 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
40 int options; /**< [Data printer flags](@ref dataprinterflags) */
Michal Vasko52927e22020-03-16 17:26:14 +010041 const struct ly_ctx *ctx; /**< libyang context */
42 struct ly_set prefix; /**< printed namespace prefixes */
43 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020044};
45
46#define LEVEL ctx->level /**< current level */
47#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
48#define LEVEL_INC if (LEVEL) {LEVEL++;} /**< increase indentation level */
49#define LEVEL_DEC if (LEVEL) {LEVEL--;} /**< decrease indentation level */
50
Michal Vasko52927e22020-03-16 17:26:14 +010051#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejcie7b95092019-05-15 11:03:07 +020052
53/**
Michal Vasko52927e22020-03-16 17:26:14 +010054 * @brief Print a namespace if not already printed.
55 *
56 * @param[in] ctx XML printer context.
57 * @param[in] ns Namespace to print, expected to be in dictionary.
58 * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
59 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
60 * @return Printed prefix of the namespace to use.
Radek Krejcie7b95092019-05-15 11:03:07 +020061 */
Michal Vasko52927e22020-03-16 17:26:14 +010062static const char *
63xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, int prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020064{
Michal Vasko52927e22020-03-16 17:26:14 +010065 int i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010066
Michal Vasko52927e22020-03-16 17:26:14 +010067 for (i = ctx->ns.count - 1; i > -1; --i) {
68 if (!new_prefix) {
69 /* find default namespace */
70 if (!ctx->prefix.objs[i]) {
71 if (ctx->ns.objs[i] != ns) {
72 /* different default namespace */
73 i = -1;
Radek Krejcie7b95092019-05-15 11:03:07 +020074 }
Michal Vasko52927e22020-03-16 17:26:14 +010075 break;
76 }
77 } else {
78 /* find prefixed namespace */
79 if (ctx->ns.objs[i] == ns) {
80 if (!ctx->prefix.objs[i]) {
81 /* default namespace is not interesting */
82 continue;
83 }
84
85 if (!strcmp(ctx->prefix.objs[i], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
86 /* the same prefix or can be any */
87 break;
88 }
89 }
Radek Krejcie7b95092019-05-15 11:03:07 +020090 }
Radek Krejcie7b95092019-05-15 11:03:07 +020091 }
Radek Krejci28681fa2019-09-06 13:08:45 +020092
Michal Vasko52927e22020-03-16 17:26:14 +010093 if (i == -1) {
94 /* suitable namespace not found, must be printed */
Radek Krejci241f6b52020-05-21 18:13:49 +020095 ly_print(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020096
Michal Vasko52927e22020-03-16 17:26:14 +010097 /* and added into namespaces */
98 if (new_prefix) {
99 new_prefix = lydict_insert(ctx->ctx, new_prefix, 0);
100 }
101 ly_set_add(&ctx->prefix, (void *)new_prefix, LY_SET_OPT_USEASLIST);
102 i = ly_set_add(&ctx->ns, (void *)ns, LY_SET_OPT_USEASLIST);
Radek Krejcie7b95092019-05-15 11:03:07 +0200103 }
Michal Vasko52927e22020-03-16 17:26:14 +0100104
105 /* return it */
106 return ctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200107}
108
109/**
110 * @brief XML mapping of YANG modules to prefixes in values.
111 *
112 * Implementation of ly_clb_get_prefix
113 */
114static const char *
115xml_print_get_prefix(const struct lys_module *mod, void *private)
116{
117 struct ly_set *ns_list = (struct ly_set*)private;
118
119 ly_set_add(ns_list, (void*)mod, 0);
120 return mod->prefix;
Radek Krejcie7b95092019-05-15 11:03:07 +0200121}
122
123/**
124 * TODO
125 */
Michal Vasko52927e22020-03-16 17:26:14 +0100126static void
Michal Vasko9f96a052020-03-10 09:41:45 +0100127xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200128{
Michal Vasko9f96a052020-03-10 09:41:45 +0100129 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100130 const struct lys_module *mod;
131 struct ly_set ns_list = {0};
Radek Krejci28681fa2019-09-06 13:08:45 +0200132#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200133 const char **prefs, **nss;
134 const char *xml_expr = NULL, *mod_name;
135 uint32_t ns_count, i;
136 int rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200137 char *p;
138 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200139#endif
Radek Krejci28681fa2019-09-06 13:08:45 +0200140 int dynamic;
141 unsigned int u;
Radek Krejcie7b95092019-05-15 11:03:07 +0200142
Radek Krejcie7b95092019-05-15 11:03:07 +0200143 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100144 if (node->schema->nodetype & LYD_NODE_TERM) {
145 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG))) ||
146 ((ctx->options & LYDP_WD_ALL_TAG) && ly_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100147 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
148 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
149 if (mod) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200150 ly_print(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200151 }
152 }
153 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100154#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200155 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
156 if (!strcmp(node->schema->name, "filter")
157 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
158 rpc_filter = 1;
159 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200160#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100161 for (meta = node->meta; meta; meta = meta->next) {
162 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 +0200163
Michal Vasko52927e22020-03-16 17:26:14 +0100164 /* print namespaces connected with the value's prefixes */
Radek Krejci28681fa2019-09-06 13:08:45 +0200165 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100166 mod = (const struct lys_module *)ns_list.objs[u];
167 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200168 }
169 ly_set_erase(&ns_list, NULL);
170
171#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200172 if (rpc_filter) {
173 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100174 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200175 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100176 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200177 if (!xml_expr) {
178 /* error */
179 return EXIT_FAILURE;
180 }
181
182 for (i = 0; i < ns_count; ++i) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200183 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
Radek Krejcie7b95092019-05-15 11:03:07 +0200184 }
185 free(prefs);
186 free(nss);
187 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200188 ly_print(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200189 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200190#endif
Michal Vasko52927e22020-03-16 17:26:14 +0100191 /* print the metadata with its namespace */
192 mod = meta->annotation->module;
Radek Krejci241f6b52020-05-21 18:13:49 +0200193 ly_print(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200194#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200195 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200196#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200197
Michal Vasko52927e22020-03-16 17:26:14 +0100198 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200199 if (value && value[0]) {
200 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200201 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200202 ly_print(ctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200203 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100204 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200205 }
206 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200207}
208
209/**
210 * @brief Print generic XML element despite of the data node type.
211 *
212 * Prints the element name, attributes and necessary namespaces.
213 *
214 * @param[in] ctx XML printer context.
215 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200216 */
Michal Vasko52927e22020-03-16 17:26:14 +0100217static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200218xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
219{
Michal Vasko52927e22020-03-16 17:26:14 +0100220 /* print node name */
Radek Krejci241f6b52020-05-21 18:13:49 +0200221 ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100222
223 /* print default namespace */
224 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
225
226 /* print metadata */
227 xml_print_meta(ctx, node);
228}
229
230static LY_ERR
231xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
232{
233 const struct ly_attr *attr;
234 const char *pref;
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200235 LY_ARRAY_SIZE_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100236
237 LY_LIST_FOR(node->attr, attr) {
238 pref = NULL;
239 if (attr->prefix.pref) {
240 /* print attribute namespace */
241 switch (attr->format) {
242 case LYD_XML:
243 pref = xml_print_ns(ctx, attr->prefix.ns, attr->prefix.pref, 0);
244 break;
245 case LYD_SCHEMA:
Michal Vasko60ea6352020-06-29 13:39:39 +0200246 case LYD_LYB:
Michal Vasko52927e22020-03-16 17:26:14 +0100247 /* cannot be created */
248 LOGINT(node->ctx);
249 return LY_EINT;
250 }
251 }
252
253 /* print namespaces connected with the value's prefixes */
254 if (attr->val_prefs) {
255 LY_ARRAY_FOR(attr->val_prefs, u) {
256 xml_print_ns(ctx, attr->val_prefs[u].ns, attr->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
257 }
258 }
259
260 /* print the attribute with its prefix and value */
Radek Krejci241f6b52020-05-21 18:13:49 +0200261 ly_print(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200262 }
263
Michal Vasko52927e22020-03-16 17:26:14 +0100264 return LY_SUCCESS;
265}
266
267static LY_ERR
268xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
269{
270 /* print node name */
Radek Krejci241f6b52020-05-21 18:13:49 +0200271 ly_print(ctx->out, "%*s<%s", INDENT, node->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100272
273 /* print default namespace */
274 switch (node->format) {
275 case LYD_XML:
276 xml_print_ns(ctx, node->prefix.ns, NULL, 0);
277 break;
278 case LYD_SCHEMA:
Michal Vasko60ea6352020-06-29 13:39:39 +0200279 case LYD_LYB:
Michal Vasko52927e22020-03-16 17:26:14 +0100280 /* cannot be created */
281 LOGINT(node->ctx);
282 return LY_EINT;
Radek Krejcie7b95092019-05-15 11:03:07 +0200283 }
284
Michal Vasko52927e22020-03-16 17:26:14 +0100285 /* print attributes */
286 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200287
288 return LY_SUCCESS;
289}
290
291static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
292
293/**
294 * @brief Print XML element representing lyd_node_term.
295 *
296 * @param[in] ctx XML printer context.
297 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200298 */
Michal Vasko52927e22020-03-16 17:26:14 +0100299static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200300xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
301{
Radek Krejcia1911222019-07-22 17:24:50 +0200302 struct ly_set ns_list = {0};
303 unsigned int u;
304 int dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200305 const char *value;
306
Michal Vasko52927e22020-03-16 17:26:14 +0100307 xml_print_node_open(ctx, (struct lyd_node *)node);
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100308 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 +0200309
Radek Krejcia1911222019-07-22 17:24:50 +0200310 /* print namespaces connected with the values's prefixes */
311 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100312 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Radek Krejci241f6b52020-05-21 18:13:49 +0200313 ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200314 }
Radek Krejcia1911222019-07-22 17:24:50 +0200315 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200316
Radek Krejcia1911222019-07-22 17:24:50 +0200317 if (!value || !value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200318 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200319 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200320 ly_print(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200321 lyxml_dump_text(ctx->out, value, 0);
Radek Krejci241f6b52020-05-21 18:13:49 +0200322 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200323 }
Radek Krejcia1911222019-07-22 17:24:50 +0200324 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100325 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200326 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200327}
328
329/**
330 * @brief Print XML element representing lyd_node_inner.
331 *
332 * @param[in] ctx XML printer context.
333 * @param[in] node Data node to be printed.
334 * @return LY_ERR value.
335 */
336static LY_ERR
337xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
338{
339 LY_ERR ret;
340 struct lyd_node *child;
341
Michal Vasko52927e22020-03-16 17:26:14 +0100342 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200343
344 if (!node->child) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200345 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200346 return LY_SUCCESS;
347 }
348
349 /* children */
Radek Krejci241f6b52020-05-21 18:13:49 +0200350 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200351
352 LEVEL_INC;
353 LY_LIST_FOR(node->child, child) {
354 ret = xml_print_node(ctx, child);
355 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
356 }
357 LEVEL_DEC;
358
Radek Krejci241f6b52020-05-21 18:13:49 +0200359 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200360
361 return LY_SUCCESS;
362}
363
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200364static LY_ERR
365xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200366{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200367 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200368 struct lyd_node *iter;
Michal Vasko60ea6352020-06-29 13:39:39 +0200369 int prev_opts, prev_lo;
Michal Vasko52927e22020-03-16 17:26:14 +0100370 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200371
Michal Vasko52927e22020-03-16 17:26:14 +0100372 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200373
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200374 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200375 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200376no_content:
Radek Krejci241f6b52020-05-21 18:13:49 +0200377 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200378 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200379 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200380 if (any->value_type == LYD_ANYDATA_LYB) {
381 /* turn logging off */
382 prev_lo = ly_log_options(0);
383
384 /* try to parse it into a data tree */
385 iter = lyd_parse_mem((struct ly_ctx *)LYD_NODE_CTX(node), any->value.mem, LYD_LYB,
386 LYD_OPT_PARSE_ONLY | LYD_OPT_OPAQ | LYD_OPT_STRICT);
387 ly_log_options(prev_lo);
388 if (!ly_errcode(LYD_NODE_CTX(node))) {
389 /* successfully parsed */
390 free(any->value.mem);
391 any->value.tree = iter;
392 any->value_type = LYD_ANYDATA_DATATREE;
393 }
394 }
395
Radek Krejcie7b95092019-05-15 11:03:07 +0200396 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200397 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200398 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100399 prev_opts = ctx->options;
Michal Vasko1ce933a2020-03-30 12:38:22 +0200400 ctx->options &= ~LYDP_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200401 LEVEL_INC;
402
Radek Krejci241f6b52020-05-21 18:13:49 +0200403 ly_print(ctx->out, ">%s", LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200404 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100405 ret = xml_print_node(ctx, iter);
406 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200407 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200408
409 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100410 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200411 break;
412 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200413 /* escape XML-sensitive characters */
414 if (!any->value.str[0]) {
415 goto no_content;
416 }
417 /* close opening tag and print data */
Radek Krejci241f6b52020-05-21 18:13:49 +0200418 ly_print(ctx->out, ">");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200419 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200420 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200421 case LYD_ANYDATA_XML:
422 /* print without escaping special characters */
423 if (!any->value.str[0]) {
424 goto no_content;
425 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200426 ly_print(ctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200427 break;
428 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200429 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200430 /* JSON and LYB format is not supported */
Michal Vasko60ea6352020-06-29 13:39:39 +0200431 LOGWRN(LYD_NODE_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200432 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200433 }
434
435 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200436 if (any->value_type == LYD_ANYDATA_DATATREE) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200437 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200438 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200439 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200440 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200441 }
442
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200443 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200444}
Radek Krejcie7b95092019-05-15 11:03:07 +0200445
Michal Vasko52927e22020-03-16 17:26:14 +0100446static LY_ERR
447xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
448{
449 LY_ERR ret;
450 struct lyd_node *child;
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200451 LY_ARRAY_SIZE_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100452
453 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
454
455 if (node->value[0]) {
456 /* print namespaces connected with the value's prefixes */
457 if (node->val_prefs) {
458 LY_ARRAY_FOR(node->val_prefs, u) {
459 xml_print_ns(ctx, node->val_prefs[u].ns, node->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
460 }
461 }
462
Radek Krejci241f6b52020-05-21 18:13:49 +0200463 ly_print(ctx->out, ">%s", node->value);
Michal Vasko52927e22020-03-16 17:26:14 +0100464 }
465
466 if (node->child) {
467 /* children */
468 if (!node->value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200469 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100470 }
471
472 LEVEL_INC;
473 LY_LIST_FOR(node->child, child) {
474 ret = xml_print_node(ctx, child);
475 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
476 }
477 LEVEL_DEC;
478
Radek Krejci241f6b52020-05-21 18:13:49 +0200479 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->name, LEVEL ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100480 } else if (node->value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200481 ly_print(ctx->out, "</%s>%s", node->name, LEVEL ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100482 } else {
483 /* no value or children */
Radek Krejci241f6b52020-05-21 18:13:49 +0200484 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100485 }
486
487 return LY_SUCCESS;
488}
489
Radek Krejcie7b95092019-05-15 11:03:07 +0200490/**
491 * @brief Print XML element representing lyd_node.
492 *
493 * @param[in] ctx XML printer context.
494 * @param[in] node Data node to be printed.
495 * @return LY_ERR value.
496 */
497static LY_ERR
498xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
499{
500 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100501 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200502
Michal Vasko9b368d32020-02-14 13:53:31 +0100503 if (!ly_should_print(node, ctx->options)) {
504 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100505 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200506 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200507
Michal Vasko52927e22020-03-16 17:26:14 +0100508 /* remember namespace definition count on this level */
509 ns_count = ctx->ns.count;
510
511 if (!node->schema) {
512 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
513 } else {
514 switch (node->schema->nodetype) {
515 case LYS_CONTAINER:
516 case LYS_LIST:
517 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100518 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100519 case LYS_ACTION:
520 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
521 break;
522 case LYS_LEAF:
523 case LYS_LEAFLIST:
524 xml_print_term(ctx, (const struct lyd_node_term *)node);
525 break;
526 case LYS_ANYXML:
527 case LYS_ANYDATA:
528 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
529 break;
530 default:
531 LOGINT(node->schema->module->ctx);
532 ret = LY_EINT;
533 break;
534 }
535 }
536
537 /* remove all added namespaces */
538 while (ns_count < ctx->ns.count) {
539 FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
540 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
541 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200542 }
543
544 return ret;
545}
546
547LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200548xml_print_data(struct ly_out *out, const struct lyd_node *root, int options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200549{
550 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100551 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200552
553 if (!root) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200554 if (out->type == LY_OUT_MEMORY || out->type == LY_OUT_CALLBACK) {
555 ly_print(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200556 }
557 goto finish;
558 }
559
Michal Vasko52927e22020-03-16 17:26:14 +0100560 ctx.out = out;
561 ctx.level = (options & LYDP_FORMAT ? 1 : 0);
562 ctx.options = options;
563 ctx.ctx = LYD_NODE_CTX(root);
564
Radek Krejcie7b95092019-05-15 11:03:07 +0200565 /* content */
566 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100567 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200568 if (!(options & LYDP_WITHSIBLINGS)) {
569 break;
570 }
571 }
572
573finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100574 assert(!ctx.prefix.count && !ctx.ns.count);
575 ly_set_erase(&ctx.prefix, NULL);
576 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200577 ly_print_flush(out);
578 return LY_SUCCESS;
579}
580