blob: 994496a8322b30f6436961f4bc3c61fd8cf6b658 [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 Krejci47fab892020-11-05 17:02:41 +010025#include "out.h"
26#include "out_internal.h"
Radek Krejci7931b192020-06-25 17:05:03 +020027#include "parser_data.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020028#include "plugins_types.h"
29#include "printer_data.h"
30#include "printer_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020031#include "set.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020032#include "tree.h"
Radek Krejci47fab892020-11-05 17:02:41 +010033#include "tree_data.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020034#include "tree_schema.h"
35#include "xml.h"
36
37/**
38 * @brief XML printer context.
39 */
40struct xmlpr_ctx {
Radek Krejci1deb5be2020-08-26 16:43:36 +020041 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) */
Michal Vasko52927e22020-03-16 17:26:14 +010044 const struct ly_ctx *ctx; /**< libyang context */
Radek Krejci1deb5be2020-08-26 16:43:36 +020045 struct ly_set prefix; /**< printed namespace prefixes */
46 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020047};
48
Michal Vasko52927e22020-03-16 17:26:14 +010049#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejci1798aae2020-07-14 13:26:06 +020050#define LYXML_PREFIX_DEFAULT 0x02 /**< The namespace is required to be a default (without prefix) */
Radek Krejcie7b95092019-05-15 11:03:07 +020051
52/**
Michal Vasko52927e22020-03-16 17:26:14 +010053 * @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.
Radek Krejcie7b95092019-05-15 11:03:07 +020060 */
Michal Vasko52927e22020-03-16 17:26:14 +010061static const char *
Radek Krejci1deb5be2020-08-26 16:43:36 +020062xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, uint32_t prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020063{
Radek Krejciba03a5a2020-08-27 14:40:41 +020064 uint32_t i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010065
Radek Krejciba03a5a2020-08-27 14:40:41 +020066 for (i = ctx->ns.count; i > 0; --i) {
Michal Vasko52927e22020-03-16 17:26:14 +010067 if (!new_prefix) {
68 /* find default namespace */
Radek Krejciba03a5a2020-08-27 14:40:41 +020069 if (!ctx->prefix.objs[i - 1]) {
70 if (ctx->ns.objs[i - 1] == ns) {
71 /* matching default namespace */
72 return ctx->prefix.objs[i - 1];
Radek Krejcie7b95092019-05-15 11:03:07 +020073 }
Radek Krejciba03a5a2020-08-27 14:40:41 +020074 /* not matching default namespace */
Michal Vasko52927e22020-03-16 17:26:14 +010075 break;
76 }
77 } else {
78 /* find prefixed namespace */
Radek Krejciba03a5a2020-08-27 14:40:41 +020079 if (ctx->ns.objs[i - 1] == ns) {
80 if (!ctx->prefix.objs[i - 1]) {
Michal Vasko52927e22020-03-16 17:26:14 +010081 /* default namespace is not interesting */
82 continue;
83 }
84
Radek Krejciba03a5a2020-08-27 14:40:41 +020085 if (!strcmp(ctx->prefix.objs[i - 1], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
Michal Vasko52927e22020-03-16 17:26:14 +010086 /* the same prefix or can be any */
Radek Krejciba03a5a2020-08-27 14:40:41 +020087 return ctx->prefix.objs[i - 1];
Michal Vasko52927e22020-03-16 17:26:14 +010088 }
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
Radek Krejciba03a5a2020-08-27 14:40:41 +020093 /* suitable namespace not found, must be printed */
94 ly_print_(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020095
Radek Krejciba03a5a2020-08-27 14:40:41 +020096 /* and added into namespaces */
97 if (new_prefix) {
Radek Krejci011e4aa2020-09-04 15:22:31 +020098 LY_CHECK_RET(lydict_insert(ctx->ctx, new_prefix, 0, &new_prefix), NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +020099 }
Radek Krejci3d92e442020-10-12 12:48:13 +0200100 LY_CHECK_RET(ly_set_add(&ctx->prefix, (void *)new_prefix, 1, NULL), NULL);
101 LY_CHECK_RET(ly_set_add(&ctx->ns, (void *)ns, 1, &i), NULL);
Michal Vasko52927e22020-03-16 17:26:14 +0100102
103 /* return it */
104 return ctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200105}
106
Michal Vasko22df3f02020-08-24 13:29:22 +0200107static const char *
Radek Krejci1deb5be2020-08-26 16:43:36 +0200108xml_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 +0200109{
Radek Krejci1798aae2020-07-14 13:26:06 +0200110 switch (format) {
111 case LYD_XML:
112 return xml_print_ns(ctx, prefix->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : prefix->id, prefix_opts);
113 break;
114 case LYD_JSON:
115 if (prefix->module_name) {
116 const struct lys_module *mod = ly_ctx_get_module_latest(ctx->ctx, prefix->module_name);
117 if (mod) {
118 return xml_print_ns(ctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : prefix->id, prefix_opts);
119 }
120 }
121 break;
Radek Krejci1798aae2020-07-14 13:26:06 +0200122 case LYD_LYB:
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200123 case LYD_UNKNOWN:
Radek Krejci1798aae2020-07-14 13:26:06 +0200124 /* cannot be created */
125 LOGINT(ctx->ctx);
126 }
127
128 return NULL;
129}
130
Radek Krejci28681fa2019-09-06 13:08:45 +0200131/**
Radek Krejcie7b95092019-05-15 11:03:07 +0200132 * TODO
133 */
Michal Vasko52927e22020-03-16 17:26:14 +0100134static void
Michal Vasko9f96a052020-03-10 09:41:45 +0100135xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200136{
Michal Vasko9f96a052020-03-10 09:41:45 +0100137 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100138 const struct lys_module *mod;
139 struct ly_set ns_list = {0};
Michal Vasko69730152020-10-09 16:30:07 +0200140
Radek Krejci28681fa2019-09-06 13:08:45 +0200141#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200142 const char **prefs, **nss;
143 const char *xml_expr = NULL, *mod_name;
144 uint32_t ns_count, i;
Radek Krejci857189e2020-09-01 13:26:36 +0200145 ly_bool rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200146 char *p;
147 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200148#endif
Radek Krejci857189e2020-09-01 13:26:36 +0200149 ly_bool dynamic;
Radek Krejcie7b95092019-05-15 11:03:07 +0200150
Radek Krejcie7b95092019-05-15 11:03:07 +0200151 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100152 if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci7931b192020-06-25 17:05:03 +0200153 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
Radek Krejci19611252020-10-04 13:54:53 +0200154 ((ctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100155 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
156 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
157 if (mod) {
Michal Vasko5233e962020-08-14 14:26:20 +0200158 ly_print_(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200159 }
160 }
161 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100162#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200163 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
Michal Vasko69730152020-10-09 16:30:07 +0200164 if (!strcmp(node->schema->name, "filter") &&
165 (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200166 rpc_filter = 1;
167 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200168#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100169 for (meta = node->meta; meta; meta = meta->next) {
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200170 const char *value = meta->value.realtype->plugin->print(&meta->value, LY_PREF_XML, &ns_list, &dynamic);
Radek Krejci28681fa2019-09-06 13:08:45 +0200171
Michal Vasko52927e22020-03-16 17:26:14 +0100172 /* print namespaces connected with the value's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200173 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100174 mod = (const struct lys_module *)ns_list.objs[u];
175 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200176 }
177 ly_set_erase(&ns_list, NULL);
178
179#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200180 if (rpc_filter) {
181 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100182 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200183 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100184 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200185 if (!xml_expr) {
186 /* error */
187 return EXIT_FAILURE;
188 }
189
190 for (i = 0; i < ns_count; ++i) {
Michal Vasko5233e962020-08-14 14:26:20 +0200191 ly_print_(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
Radek Krejcie7b95092019-05-15 11:03:07 +0200192 }
193 free(prefs);
194 free(nss);
195 }
Michal Vasko5233e962020-08-14 14:26:20 +0200196 ly_print_(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200197 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200198#endif
Radek Krejci0f969882020-08-21 16:56:47 +0200199 /* print the metadata with its namespace */
200 mod = meta->annotation->module;
201 ly_print_(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200202#if 0
Radek Krejci0f969882020-08-21 16:56:47 +0200203 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200204#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200205
Michal Vasko52927e22020-03-16 17:26:14 +0100206 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200207 if (value && value[0]) {
208 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200209 }
Michal Vasko5233e962020-08-14 14:26:20 +0200210 ly_print_(ctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200211 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100212 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200213 }
214 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200215}
216
217/**
218 * @brief Print generic XML element despite of the data node type.
219 *
220 * Prints the element name, attributes and necessary namespaces.
221 *
222 * @param[in] ctx XML printer context.
223 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200224 */
Michal Vasko52927e22020-03-16 17:26:14 +0100225static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200226xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
227{
Michal Vasko52927e22020-03-16 17:26:14 +0100228 /* print node name */
Michal Vasko5233e962020-08-14 14:26:20 +0200229 ly_print_(ctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100230
231 /* print default namespace */
232 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
233
234 /* print metadata */
235 xml_print_meta(ctx, node);
236}
237
238static LY_ERR
239xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
240{
Radek Krejci5536d282020-08-04 23:27:44 +0200241 const struct lyd_attr *attr;
Michal Vasko52927e22020-03-16 17:26:14 +0100242 const char *pref;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200243 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100244
245 LY_LIST_FOR(node->attr, attr) {
246 pref = NULL;
Radek Krejci1798aae2020-07-14 13:26:06 +0200247 if (attr->prefix.id) {
Michal Vasko52927e22020-03-16 17:26:14 +0100248 /* print attribute namespace */
Radek Krejci1798aae2020-07-14 13:26:06 +0200249 pref = xml_print_ns_opaq(ctx, attr->format, &attr->prefix, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100250 }
251
252 /* print namespaces connected with the value's prefixes */
253 if (attr->val_prefs) {
254 LY_ARRAY_FOR(attr->val_prefs, u) {
Radek Krejci1798aae2020-07-14 13:26:06 +0200255 xml_print_ns_opaq(ctx, attr->format, &attr->val_prefs[u], LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100256 }
257 }
258
259 /* print the attribute with its prefix and value */
Michal Vasko5233e962020-08-14 14:26:20 +0200260 ly_print_(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200261 }
262
Michal Vasko52927e22020-03-16 17:26:14 +0100263 return LY_SUCCESS;
264}
265
266static LY_ERR
267xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
268{
269 /* print node name */
Michal Vasko5233e962020-08-14 14:26:20 +0200270 ly_print_(ctx->out, "%*s<%s", INDENT, node->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100271
272 /* print default namespace */
Radek Krejci1798aae2020-07-14 13:26:06 +0200273 xml_print_ns_opaq(ctx, node->format, &node->prefix, LYXML_PREFIX_DEFAULT);
Radek Krejcie7b95092019-05-15 11:03:07 +0200274
Michal Vasko52927e22020-03-16 17:26:14 +0100275 /* print attributes */
276 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200277
278 return LY_SUCCESS;
279}
280
281static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
282
283/**
284 * @brief Print XML element representing lyd_node_term.
285 *
286 * @param[in] ctx XML printer context.
287 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200288 */
Michal Vasko52927e22020-03-16 17:26:14 +0100289static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200290xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
291{
Radek Krejcia1911222019-07-22 17:24:50 +0200292 struct ly_set ns_list = {0};
Radek Krejci857189e2020-09-01 13:26:36 +0200293 ly_bool dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200294 const char *value;
295
Michal Vasko52927e22020-03-16 17:26:14 +0100296 xml_print_node_open(ctx, (struct lyd_node *)node);
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200297 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 +0200298
Radek Krejcia1911222019-07-22 17:24:50 +0200299 /* print namespaces connected with the values's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200300 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100301 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Michal Vasko5233e962020-08-14 14:26:20 +0200302 ly_print_(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200303 }
Radek Krejcia1911222019-07-22 17:24:50 +0200304 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200305
Radek Krejcia1911222019-07-22 17:24:50 +0200306 if (!value || !value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200307 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200308 } else {
Michal Vasko5233e962020-08-14 14:26:20 +0200309 ly_print_(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200310 lyxml_dump_text(ctx->out, value, 0);
Michal Vasko5233e962020-08-14 14:26:20 +0200311 ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200312 }
Radek Krejcia1911222019-07-22 17:24:50 +0200313 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100314 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200315 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200316}
317
318/**
319 * @brief Print XML element representing lyd_node_inner.
320 *
321 * @param[in] ctx XML printer context.
322 * @param[in] node Data node to be printed.
323 * @return LY_ERR value.
324 */
325static LY_ERR
326xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
327{
328 LY_ERR ret;
329 struct lyd_node *child;
330
Michal Vasko52927e22020-03-16 17:26:14 +0100331 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200332
333 if (!node->child) {
Michal Vasko5233e962020-08-14 14:26:20 +0200334 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200335 return LY_SUCCESS;
336 }
337
338 /* children */
Michal Vasko5233e962020-08-14 14:26:20 +0200339 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200340
341 LEVEL_INC;
342 LY_LIST_FOR(node->child, child) {
343 ret = xml_print_node(ctx, child);
344 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
345 }
346 LEVEL_DEC;
347
Michal Vasko5233e962020-08-14 14:26:20 +0200348 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200349
350 return LY_SUCCESS;
351}
352
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200353static LY_ERR
354xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200355{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200356 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200357 struct lyd_node *iter;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200358 uint32_t prev_opts, prev_lo;
Michal Vasko52927e22020-03-16 17:26:14 +0100359 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200360
Michal Vasko52927e22020-03-16 17:26:14 +0100361 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200362
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200363 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200364 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200365no_content:
Michal Vasko5233e962020-08-14 14:26:20 +0200366 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200367 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200368 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200369 if (any->value_type == LYD_ANYDATA_LYB) {
370 /* turn logging off */
371 prev_lo = ly_log_options(0);
372
373 /* try to parse it into a data tree */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200374 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 +0200375 /* successfully parsed */
376 free(any->value.mem);
377 any->value.tree = iter;
378 any->value_type = LYD_ANYDATA_DATATREE;
379 }
Radek Krejci7931b192020-06-25 17:05:03 +0200380
381 /* turn loggin on again */
382 ly_log_options(prev_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200383 }
384
Radek Krejcie7b95092019-05-15 11:03:07 +0200385 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200386 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200387 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100388 prev_opts = ctx->options;
Radek Krejci7931b192020-06-25 17:05:03 +0200389 ctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200390 LEVEL_INC;
391
Michal Vasko5233e962020-08-14 14:26:20 +0200392 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200393 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100394 ret = xml_print_node(ctx, iter);
395 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200396 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200397
398 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100399 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200400 break;
401 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200402 /* escape XML-sensitive characters */
403 if (!any->value.str[0]) {
404 goto no_content;
405 }
406 /* close opening tag and print data */
Michal Vasko5233e962020-08-14 14:26:20 +0200407 ly_print_(ctx->out, ">");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200408 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200409 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200410 case LYD_ANYDATA_XML:
411 /* print without escaping special characters */
412 if (!any->value.str[0]) {
413 goto no_content;
414 }
Michal Vasko5233e962020-08-14 14:26:20 +0200415 ly_print_(ctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200416 break;
417 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200418 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200419 /* JSON and LYB format is not supported */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200420 LOGWRN(LYD_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200421 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200422 }
423
424 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200425 if (any->value_type == LYD_ANYDATA_DATATREE) {
Michal Vasko5233e962020-08-14 14:26:20 +0200426 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200427 } else {
Michal Vasko5233e962020-08-14 14:26:20 +0200428 ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200429 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200430 }
431
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200432 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200433}
Radek Krejcie7b95092019-05-15 11:03:07 +0200434
Michal Vasko52927e22020-03-16 17:26:14 +0100435static LY_ERR
436xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
437{
438 LY_ERR ret;
439 struct lyd_node *child;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200440 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100441
442 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
443
444 if (node->value[0]) {
445 /* print namespaces connected with the value's prefixes */
446 if (node->val_prefs) {
447 LY_ARRAY_FOR(node->val_prefs, u) {
Radek Krejci1798aae2020-07-14 13:26:06 +0200448 xml_print_ns_opaq(ctx, node->format, &node->val_prefs[u], LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100449 }
450 }
451
Michal Vasko5233e962020-08-14 14:26:20 +0200452 ly_print_(ctx->out, ">%s", node->value);
Michal Vasko52927e22020-03-16 17:26:14 +0100453 }
454
455 if (node->child) {
456 /* children */
457 if (!node->value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200458 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100459 }
460
461 LEVEL_INC;
462 LY_LIST_FOR(node->child, child) {
463 ret = xml_print_node(ctx, child);
464 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
465 }
466 LEVEL_DEC;
467
Michal Vasko5233e962020-08-14 14:26:20 +0200468 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100469 } else if (node->value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200470 ly_print_(ctx->out, "</%s>%s", node->name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100471 } else {
472 /* no value or children */
Michal Vasko5233e962020-08-14 14:26:20 +0200473 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100474 }
475
476 return LY_SUCCESS;
477}
478
Radek Krejcie7b95092019-05-15 11:03:07 +0200479/**
480 * @brief Print XML element representing lyd_node.
481 *
482 * @param[in] ctx XML printer context.
483 * @param[in] node Data node to be printed.
484 * @return LY_ERR value.
485 */
486static LY_ERR
487xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
488{
489 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100490 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200491
Michal Vasko9b368d32020-02-14 13:53:31 +0100492 if (!ly_should_print(node, ctx->options)) {
493 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100494 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200495 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200496
Michal Vasko52927e22020-03-16 17:26:14 +0100497 /* remember namespace definition count on this level */
498 ns_count = ctx->ns.count;
499
500 if (!node->schema) {
501 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
502 } else {
503 switch (node->schema->nodetype) {
504 case LYS_CONTAINER:
505 case LYS_LIST:
506 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100507 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100508 case LYS_ACTION:
509 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
510 break;
511 case LYS_LEAF:
512 case LYS_LEAFLIST:
513 xml_print_term(ctx, (const struct lyd_node_term *)node);
514 break;
515 case LYS_ANYXML:
516 case LYS_ANYDATA:
517 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
518 break;
519 default:
520 LOGINT(node->schema->module->ctx);
521 ret = LY_EINT;
522 break;
523 }
524 }
525
526 /* remove all added namespaces */
527 while (ns_count < ctx->ns.count) {
528 FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
529 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
530 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200531 }
532
533 return ret;
534}
535
536LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200537xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200538{
539 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100540 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200541
542 if (!root) {
Michal Vasko69730152020-10-09 16:30:07 +0200543 if ((out->type == LY_OUT_MEMORY) || (out->type == LY_OUT_CALLBACK)) {
Michal Vasko5233e962020-08-14 14:26:20 +0200544 ly_print_(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200545 }
546 goto finish;
547 }
548
Michal Vasko52927e22020-03-16 17:26:14 +0100549 ctx.out = out;
Radek Krejci52f65552020-09-01 17:03:35 +0200550 ctx.level = (options & LYD_PRINT_SHRINK ? 0 : 1);
Michal Vasko52927e22020-03-16 17:26:14 +0100551 ctx.options = options;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200552 ctx.ctx = LYD_CTX(root);
Michal Vasko52927e22020-03-16 17:26:14 +0100553
Radek Krejcie7b95092019-05-15 11:03:07 +0200554 /* content */
555 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100556 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejci7931b192020-06-25 17:05:03 +0200557 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200558 break;
559 }
560 }
561
562finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100563 assert(!ctx.prefix.count && !ctx.ns.count);
564 ly_set_erase(&ctx.prefix, NULL);
565 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200566 ly_print_flush(out);
567 return LY_SUCCESS;
568}