blob: f3d2c524c1e2f0d4309efbec4b0c5e4ddd6c3f5b [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"
Michal Vaskoafac7822020-10-20 14:22:26 +020027#include "out_internal.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 Krejci1deb5be2020-08-26 16:43:36 +020039 struct ly_out *out; /**< output specification */
40 uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
41 uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */
Michal Vasko52927e22020-03-16 17:26:14 +010042 const struct ly_ctx *ctx; /**< libyang context */
Radek Krejci1deb5be2020-08-26 16:43:36 +020043 struct ly_set prefix; /**< printed namespace prefixes */
44 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020045};
46
Michal Vasko52927e22020-03-16 17:26:14 +010047#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejci1798aae2020-07-14 13:26:06 +020048#define LYXML_PREFIX_DEFAULT 0x02 /**< The namespace is required to be a default (without prefix) */
Radek Krejcie7b95092019-05-15 11:03:07 +020049
50/**
Michal Vasko52927e22020-03-16 17:26:14 +010051 * @brief Print a namespace if not already printed.
52 *
53 * @param[in] ctx XML printer context.
54 * @param[in] ns Namespace to print, expected to be in dictionary.
55 * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
56 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
57 * @return Printed prefix of the namespace to use.
Radek Krejcie7b95092019-05-15 11:03:07 +020058 */
Michal Vasko52927e22020-03-16 17:26:14 +010059static const char *
Radek Krejci1deb5be2020-08-26 16:43:36 +020060xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, uint32_t prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020061{
Radek Krejciba03a5a2020-08-27 14:40:41 +020062 uint32_t i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010063
Radek Krejciba03a5a2020-08-27 14:40:41 +020064 for (i = ctx->ns.count; i > 0; --i) {
Michal Vasko52927e22020-03-16 17:26:14 +010065 if (!new_prefix) {
66 /* find default namespace */
Radek Krejciba03a5a2020-08-27 14:40:41 +020067 if (!ctx->prefix.objs[i - 1]) {
68 if (ctx->ns.objs[i - 1] == ns) {
69 /* matching default namespace */
70 return ctx->prefix.objs[i - 1];
Radek Krejcie7b95092019-05-15 11:03:07 +020071 }
Radek Krejciba03a5a2020-08-27 14:40:41 +020072 /* not matching default namespace */
Michal Vasko52927e22020-03-16 17:26:14 +010073 break;
74 }
75 } else {
76 /* find prefixed namespace */
Radek Krejciba03a5a2020-08-27 14:40:41 +020077 if (ctx->ns.objs[i - 1] == ns) {
78 if (!ctx->prefix.objs[i - 1]) {
Michal Vasko52927e22020-03-16 17:26:14 +010079 /* default namespace is not interesting */
80 continue;
81 }
82
Radek Krejciba03a5a2020-08-27 14:40:41 +020083 if (!strcmp(ctx->prefix.objs[i - 1], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
Michal Vasko52927e22020-03-16 17:26:14 +010084 /* the same prefix or can be any */
Radek Krejciba03a5a2020-08-27 14:40:41 +020085 return ctx->prefix.objs[i - 1];
Michal Vasko52927e22020-03-16 17:26:14 +010086 }
87 }
Radek Krejcie7b95092019-05-15 11:03:07 +020088 }
Radek Krejcie7b95092019-05-15 11:03:07 +020089 }
Radek Krejci28681fa2019-09-06 13:08:45 +020090
Radek Krejciba03a5a2020-08-27 14:40:41 +020091 /* suitable namespace not found, must be printed */
92 ly_print_(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020093
Radek Krejciba03a5a2020-08-27 14:40:41 +020094 /* and added into namespaces */
95 if (new_prefix) {
Radek Krejci011e4aa2020-09-04 15:22:31 +020096 LY_CHECK_RET(lydict_insert(ctx->ctx, new_prefix, 0, &new_prefix), NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +020097 }
Radek Krejci3d92e442020-10-12 12:48:13 +020098 LY_CHECK_RET(ly_set_add(&ctx->prefix, (void *)new_prefix, 1, NULL), NULL);
99 LY_CHECK_RET(ly_set_add(&ctx->ns, (void *)ns, 1, &i), NULL);
Michal Vasko52927e22020-03-16 17:26:14 +0100100
101 /* return it */
102 return ctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200103}
104
Michal Vasko22df3f02020-08-24 13:29:22 +0200105static const char *
Radek Krejci1deb5be2020-08-26 16:43:36 +0200106xml_print_ns_opaq(struct xmlpr_ctx *ctx, LYD_FORMAT format, const struct ly_prefix *prefix, uint32_t prefix_opts)
Radek Krejci1798aae2020-07-14 13:26:06 +0200107{
Radek Krejci1798aae2020-07-14 13:26:06 +0200108 switch (format) {
109 case LYD_XML:
110 return xml_print_ns(ctx, prefix->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : prefix->id, prefix_opts);
111 break;
112 case LYD_JSON:
113 if (prefix->module_name) {
114 const struct lys_module *mod = ly_ctx_get_module_latest(ctx->ctx, prefix->module_name);
115 if (mod) {
116 return xml_print_ns(ctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : prefix->id, prefix_opts);
117 }
118 }
119 break;
Radek Krejci1798aae2020-07-14 13:26:06 +0200120 case LYD_LYB:
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200121 case LYD_UNKNOWN:
Radek Krejci1798aae2020-07-14 13:26:06 +0200122 /* cannot be created */
123 LOGINT(ctx->ctx);
124 }
125
126 return NULL;
127}
128
Radek Krejci28681fa2019-09-06 13:08:45 +0200129/**
Radek Krejcie7b95092019-05-15 11:03:07 +0200130 * TODO
131 */
Michal Vasko52927e22020-03-16 17:26:14 +0100132static void
Michal Vasko9f96a052020-03-10 09:41:45 +0100133xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200134{
Michal Vasko9f96a052020-03-10 09:41:45 +0100135 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100136 const struct lys_module *mod;
137 struct ly_set ns_list = {0};
Michal Vasko69730152020-10-09 16:30:07 +0200138
Radek Krejci28681fa2019-09-06 13:08:45 +0200139#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200140 const char **prefs, **nss;
141 const char *xml_expr = NULL, *mod_name;
142 uint32_t ns_count, i;
Radek Krejci857189e2020-09-01 13:26:36 +0200143 ly_bool rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200144 char *p;
145 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200146#endif
Radek Krejci857189e2020-09-01 13:26:36 +0200147 ly_bool dynamic;
Radek Krejcie7b95092019-05-15 11:03:07 +0200148
Radek Krejcie7b95092019-05-15 11:03:07 +0200149 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100150 if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci7931b192020-06-25 17:05:03 +0200151 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
Radek Krejci19611252020-10-04 13:54:53 +0200152 ((ctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100153 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
154 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
155 if (mod) {
Michal Vasko5233e962020-08-14 14:26:20 +0200156 ly_print_(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200157 }
158 }
159 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100160#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200161 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
Michal Vasko69730152020-10-09 16:30:07 +0200162 if (!strcmp(node->schema->name, "filter") &&
163 (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200164 rpc_filter = 1;
165 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200166#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100167 for (meta = node->meta; meta; meta = meta->next) {
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200168 const char *value = meta->value.realtype->plugin->print(&meta->value, LY_PREF_XML, &ns_list, &dynamic);
Radek Krejci28681fa2019-09-06 13:08:45 +0200169
Michal Vasko52927e22020-03-16 17:26:14 +0100170 /* print namespaces connected with the value's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200171 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100172 mod = (const struct lys_module *)ns_list.objs[u];
173 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200174 }
175 ly_set_erase(&ns_list, NULL);
176
177#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200178 if (rpc_filter) {
179 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100180 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200181 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100182 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200183 if (!xml_expr) {
184 /* error */
185 return EXIT_FAILURE;
186 }
187
188 for (i = 0; i < ns_count; ++i) {
Michal Vasko5233e962020-08-14 14:26:20 +0200189 ly_print_(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
Radek Krejcie7b95092019-05-15 11:03:07 +0200190 }
191 free(prefs);
192 free(nss);
193 }
Michal Vasko5233e962020-08-14 14:26:20 +0200194 ly_print_(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200195 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200196#endif
Radek Krejci0f969882020-08-21 16:56:47 +0200197 /* print the metadata with its namespace */
198 mod = meta->annotation->module;
199 ly_print_(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200200#if 0
Radek Krejci0f969882020-08-21 16:56:47 +0200201 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200202#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200203
Michal Vasko52927e22020-03-16 17:26:14 +0100204 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200205 if (value && value[0]) {
206 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200207 }
Michal Vasko5233e962020-08-14 14:26:20 +0200208 ly_print_(ctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200209 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100210 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200211 }
212 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200213}
214
215/**
216 * @brief Print generic XML element despite of the data node type.
217 *
218 * Prints the element name, attributes and necessary namespaces.
219 *
220 * @param[in] ctx XML printer context.
221 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200222 */
Michal Vasko52927e22020-03-16 17:26:14 +0100223static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200224xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
225{
Michal Vasko52927e22020-03-16 17:26:14 +0100226 /* print node name */
Michal Vasko5233e962020-08-14 14:26:20 +0200227 ly_print_(ctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100228
229 /* print default namespace */
230 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
231
232 /* print metadata */
233 xml_print_meta(ctx, node);
234}
235
236static LY_ERR
237xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
238{
Radek Krejci5536d282020-08-04 23:27:44 +0200239 const struct lyd_attr *attr;
Michal Vasko52927e22020-03-16 17:26:14 +0100240 const char *pref;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200241 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100242
243 LY_LIST_FOR(node->attr, attr) {
244 pref = NULL;
Radek Krejci1798aae2020-07-14 13:26:06 +0200245 if (attr->prefix.id) {
Michal Vasko52927e22020-03-16 17:26:14 +0100246 /* print attribute namespace */
Radek Krejci1798aae2020-07-14 13:26:06 +0200247 pref = xml_print_ns_opaq(ctx, attr->format, &attr->prefix, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100248 }
249
250 /* print namespaces connected with the value's prefixes */
251 if (attr->val_prefs) {
252 LY_ARRAY_FOR(attr->val_prefs, u) {
Radek Krejci1798aae2020-07-14 13:26:06 +0200253 xml_print_ns_opaq(ctx, attr->format, &attr->val_prefs[u], LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100254 }
255 }
256
257 /* print the attribute with its prefix and value */
Michal Vasko5233e962020-08-14 14:26:20 +0200258 ly_print_(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200259 }
260
Michal Vasko52927e22020-03-16 17:26:14 +0100261 return LY_SUCCESS;
262}
263
264static LY_ERR
265xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
266{
267 /* print node name */
Michal Vasko5233e962020-08-14 14:26:20 +0200268 ly_print_(ctx->out, "%*s<%s", INDENT, node->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100269
270 /* print default namespace */
Radek Krejci1798aae2020-07-14 13:26:06 +0200271 xml_print_ns_opaq(ctx, node->format, &node->prefix, LYXML_PREFIX_DEFAULT);
Radek Krejcie7b95092019-05-15 11:03:07 +0200272
Michal Vasko52927e22020-03-16 17:26:14 +0100273 /* print attributes */
274 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200275
276 return LY_SUCCESS;
277}
278
279static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
280
281/**
282 * @brief Print XML element representing lyd_node_term.
283 *
284 * @param[in] ctx XML printer context.
285 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200286 */
Michal Vasko52927e22020-03-16 17:26:14 +0100287static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200288xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
289{
Radek Krejcia1911222019-07-22 17:24:50 +0200290 struct ly_set ns_list = {0};
Radek Krejci857189e2020-09-01 13:26:36 +0200291 ly_bool dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200292 const char *value;
293
Michal Vasko52927e22020-03-16 17:26:14 +0100294 xml_print_node_open(ctx, (struct lyd_node *)node);
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200295 value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(&node->value, LY_PREF_XML, &ns_list, &dynamic);
Radek Krejcie7b95092019-05-15 11:03:07 +0200296
Radek Krejcia1911222019-07-22 17:24:50 +0200297 /* print namespaces connected with the values's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200298 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100299 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Michal Vasko5233e962020-08-14 14:26:20 +0200300 ly_print_(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200301 }
Radek Krejcia1911222019-07-22 17:24:50 +0200302 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200303
Radek Krejcia1911222019-07-22 17:24:50 +0200304 if (!value || !value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200305 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200306 } else {
Michal Vasko5233e962020-08-14 14:26:20 +0200307 ly_print_(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200308 lyxml_dump_text(ctx->out, value, 0);
Michal Vasko5233e962020-08-14 14:26:20 +0200309 ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200310 }
Radek Krejcia1911222019-07-22 17:24:50 +0200311 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100312 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200313 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200314}
315
316/**
317 * @brief Print XML element representing lyd_node_inner.
318 *
319 * @param[in] ctx XML printer context.
320 * @param[in] node Data node to be printed.
321 * @return LY_ERR value.
322 */
323static LY_ERR
324xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
325{
326 LY_ERR ret;
327 struct lyd_node *child;
328
Michal Vasko52927e22020-03-16 17:26:14 +0100329 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200330
331 if (!node->child) {
Michal Vasko5233e962020-08-14 14:26:20 +0200332 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200333 return LY_SUCCESS;
334 }
335
336 /* children */
Michal Vasko5233e962020-08-14 14:26:20 +0200337 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200338
339 LEVEL_INC;
340 LY_LIST_FOR(node->child, child) {
341 ret = xml_print_node(ctx, child);
342 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
343 }
344 LEVEL_DEC;
345
Michal Vasko5233e962020-08-14 14:26:20 +0200346 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200347
348 return LY_SUCCESS;
349}
350
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200351static LY_ERR
352xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200353{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200354 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200355 struct lyd_node *iter;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200356 uint32_t prev_opts, prev_lo;
Michal Vasko52927e22020-03-16 17:26:14 +0100357 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200358
Michal Vasko52927e22020-03-16 17:26:14 +0100359 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200360
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200361 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200362 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200363no_content:
Michal Vasko5233e962020-08-14 14:26:20 +0200364 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200365 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200366 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200367 if (any->value_type == LYD_ANYDATA_LYB) {
368 /* turn logging off */
369 prev_lo = ly_log_options(0);
370
371 /* try to parse it into a data tree */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200372 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) {
Michal Vasko60ea6352020-06-29 13:39:39 +0200373 /* successfully parsed */
374 free(any->value.mem);
375 any->value.tree = iter;
376 any->value_type = LYD_ANYDATA_DATATREE;
377 }
Radek Krejci7931b192020-06-25 17:05:03 +0200378
379 /* turn loggin on again */
380 ly_log_options(prev_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200381 }
382
Radek Krejcie7b95092019-05-15 11:03:07 +0200383 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200384 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200385 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100386 prev_opts = ctx->options;
Radek Krejci7931b192020-06-25 17:05:03 +0200387 ctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200388 LEVEL_INC;
389
Michal Vasko5233e962020-08-14 14:26:20 +0200390 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200391 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100392 ret = xml_print_node(ctx, iter);
393 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200394 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200395
396 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100397 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200398 break;
399 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200400 /* escape XML-sensitive characters */
401 if (!any->value.str[0]) {
402 goto no_content;
403 }
404 /* close opening tag and print data */
Michal Vasko5233e962020-08-14 14:26:20 +0200405 ly_print_(ctx->out, ">");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200406 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200407 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200408 case LYD_ANYDATA_XML:
409 /* print without escaping special characters */
410 if (!any->value.str[0]) {
411 goto no_content;
412 }
Michal Vasko5233e962020-08-14 14:26:20 +0200413 ly_print_(ctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200414 break;
415 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200416 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200417 /* JSON and LYB format is not supported */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200418 LOGWRN(LYD_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200419 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200420 }
421
422 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200423 if (any->value_type == LYD_ANYDATA_DATATREE) {
Michal Vasko5233e962020-08-14 14:26:20 +0200424 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200425 } else {
Michal Vasko5233e962020-08-14 14:26:20 +0200426 ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200427 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200428 }
429
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200430 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200431}
Radek Krejcie7b95092019-05-15 11:03:07 +0200432
Michal Vasko52927e22020-03-16 17:26:14 +0100433static LY_ERR
434xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
435{
436 LY_ERR ret;
437 struct lyd_node *child;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200438 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100439
440 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
441
442 if (node->value[0]) {
443 /* print namespaces connected with the value's prefixes */
444 if (node->val_prefs) {
445 LY_ARRAY_FOR(node->val_prefs, u) {
Radek Krejci1798aae2020-07-14 13:26:06 +0200446 xml_print_ns_opaq(ctx, node->format, &node->val_prefs[u], LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100447 }
448 }
449
Michal Vasko5233e962020-08-14 14:26:20 +0200450 ly_print_(ctx->out, ">%s", node->value);
Michal Vasko52927e22020-03-16 17:26:14 +0100451 }
452
453 if (node->child) {
454 /* children */
455 if (!node->value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200456 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100457 }
458
459 LEVEL_INC;
460 LY_LIST_FOR(node->child, child) {
461 ret = xml_print_node(ctx, child);
462 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
463 }
464 LEVEL_DEC;
465
Michal Vasko5233e962020-08-14 14:26:20 +0200466 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100467 } else if (node->value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200468 ly_print_(ctx->out, "</%s>%s", node->name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100469 } else {
470 /* no value or children */
Michal Vasko5233e962020-08-14 14:26:20 +0200471 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100472 }
473
474 return LY_SUCCESS;
475}
476
Radek Krejcie7b95092019-05-15 11:03:07 +0200477/**
478 * @brief Print XML element representing lyd_node.
479 *
480 * @param[in] ctx XML printer context.
481 * @param[in] node Data node to be printed.
482 * @return LY_ERR value.
483 */
484static LY_ERR
485xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
486{
487 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100488 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200489
Michal Vasko9b368d32020-02-14 13:53:31 +0100490 if (!ly_should_print(node, ctx->options)) {
491 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100492 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200493 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200494
Michal Vasko52927e22020-03-16 17:26:14 +0100495 /* remember namespace definition count on this level */
496 ns_count = ctx->ns.count;
497
498 if (!node->schema) {
499 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
500 } else {
501 switch (node->schema->nodetype) {
502 case LYS_CONTAINER:
503 case LYS_LIST:
504 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100505 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100506 case LYS_ACTION:
507 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
508 break;
509 case LYS_LEAF:
510 case LYS_LEAFLIST:
511 xml_print_term(ctx, (const struct lyd_node_term *)node);
512 break;
513 case LYS_ANYXML:
514 case LYS_ANYDATA:
515 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
516 break;
517 default:
518 LOGINT(node->schema->module->ctx);
519 ret = LY_EINT;
520 break;
521 }
522 }
523
524 /* remove all added namespaces */
525 while (ns_count < ctx->ns.count) {
526 FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
527 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
528 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200529 }
530
531 return ret;
532}
533
534LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200535xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200536{
537 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100538 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200539
540 if (!root) {
Michal Vasko69730152020-10-09 16:30:07 +0200541 if ((out->type == LY_OUT_MEMORY) || (out->type == LY_OUT_CALLBACK)) {
Michal Vasko5233e962020-08-14 14:26:20 +0200542 ly_print_(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200543 }
544 goto finish;
545 }
546
Michal Vasko52927e22020-03-16 17:26:14 +0100547 ctx.out = out;
Radek Krejci52f65552020-09-01 17:03:35 +0200548 ctx.level = (options & LYD_PRINT_SHRINK ? 0 : 1);
Michal Vasko52927e22020-03-16 17:26:14 +0100549 ctx.options = options;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200550 ctx.ctx = LYD_CTX(root);
Michal Vasko52927e22020-03-16 17:26:14 +0100551
Radek Krejcie7b95092019-05-15 11:03:07 +0200552 /* content */
553 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100554 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejci7931b192020-06-25 17:05:03 +0200555 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200556 break;
557 }
558 }
559
560finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100561 assert(!ctx.prefix.count && !ctx.ns.count);
562 ly_set_erase(&ctx.prefix, NULL);
563 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200564 ly_print_flush(out);
565 return LY_SUCCESS;
566}