blob: eea1e76a116266afea35c1efb5ab1c78fa5a6704 [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 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 Krejciba03a5a2020-08-27 14:40:41 +020098 LY_CHECK_RET(ly_set_add(&ctx->prefix, (void *)new_prefix, LY_SET_OPT_USEASLIST, NULL), NULL);
99 LY_CHECK_RET(ly_set_add(&ctx->ns, (void *)ns, LY_SET_OPT_USEASLIST, &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};
Radek Krejci28681fa2019-09-06 13:08:45 +0200138#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200139 const char **prefs, **nss;
140 const char *xml_expr = NULL, *mod_name;
141 uint32_t ns_count, i;
Radek Krejci857189e2020-09-01 13:26:36 +0200142 ly_bool rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200143 char *p;
144 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200145#endif
Radek Krejci857189e2020-09-01 13:26:36 +0200146 ly_bool dynamic;
Radek Krejcie7b95092019-05-15 11:03:07 +0200147
Radek Krejcie7b95092019-05-15 11:03:07 +0200148 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100149 if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci7931b192020-06-25 17:05:03 +0200150 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
Radek Krejci19611252020-10-04 13:54:53 +0200151 ((ctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100152 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
153 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
154 if (mod) {
Michal Vasko5233e962020-08-14 14:26:20 +0200155 ly_print_(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200156 }
157 }
158 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100159#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200160 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
161 if (!strcmp(node->schema->name, "filter")
162 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
163 rpc_filter = 1;
164 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200165#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100166 for (meta = node->meta; meta; meta = meta->next) {
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200167 const char *value = meta->value.realtype->plugin->print(&meta->value, LY_PREF_XML, &ns_list, &dynamic);
Radek Krejci28681fa2019-09-06 13:08:45 +0200168
Michal Vasko52927e22020-03-16 17:26:14 +0100169 /* print namespaces connected with the value's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200170 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100171 mod = (const struct lys_module *)ns_list.objs[u];
172 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200173 }
174 ly_set_erase(&ns_list, NULL);
175
176#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200177 if (rpc_filter) {
178 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100179 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200180 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100181 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200182 if (!xml_expr) {
183 /* error */
184 return EXIT_FAILURE;
185 }
186
187 for (i = 0; i < ns_count; ++i) {
Michal Vasko5233e962020-08-14 14:26:20 +0200188 ly_print_(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
Radek Krejcie7b95092019-05-15 11:03:07 +0200189 }
190 free(prefs);
191 free(nss);
192 }
Michal Vasko5233e962020-08-14 14:26:20 +0200193 ly_print_(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200194 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200195#endif
Radek Krejci0f969882020-08-21 16:56:47 +0200196 /* print the metadata with its namespace */
197 mod = meta->annotation->module;
198 ly_print_(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200199#if 0
Radek Krejci0f969882020-08-21 16:56:47 +0200200 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200201#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200202
Michal Vasko52927e22020-03-16 17:26:14 +0100203 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200204 if (value && value[0]) {
205 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200206 }
Michal Vasko5233e962020-08-14 14:26:20 +0200207 ly_print_(ctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200208 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100209 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200210 }
211 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200212}
213
214/**
215 * @brief Print generic XML element despite of the data node type.
216 *
217 * Prints the element name, attributes and necessary namespaces.
218 *
219 * @param[in] ctx XML printer context.
220 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200221 */
Michal Vasko52927e22020-03-16 17:26:14 +0100222static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200223xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
224{
Michal Vasko52927e22020-03-16 17:26:14 +0100225 /* print node name */
Michal Vasko5233e962020-08-14 14:26:20 +0200226 ly_print_(ctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100227
228 /* print default namespace */
229 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
230
231 /* print metadata */
232 xml_print_meta(ctx, node);
233}
234
235static LY_ERR
236xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
237{
Radek Krejci5536d282020-08-04 23:27:44 +0200238 const struct lyd_attr *attr;
Michal Vasko52927e22020-03-16 17:26:14 +0100239 const char *pref;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200240 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100241
242 LY_LIST_FOR(node->attr, attr) {
243 pref = NULL;
Radek Krejci1798aae2020-07-14 13:26:06 +0200244 if (attr->prefix.id) {
Michal Vasko52927e22020-03-16 17:26:14 +0100245 /* print attribute namespace */
Radek Krejci1798aae2020-07-14 13:26:06 +0200246 pref = xml_print_ns_opaq(ctx, attr->format, &attr->prefix, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100247 }
248
249 /* print namespaces connected with the value's prefixes */
250 if (attr->val_prefs) {
251 LY_ARRAY_FOR(attr->val_prefs, u) {
Radek Krejci1798aae2020-07-14 13:26:06 +0200252 xml_print_ns_opaq(ctx, attr->format, &attr->val_prefs[u], LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100253 }
254 }
255
256 /* print the attribute with its prefix and value */
Michal Vasko5233e962020-08-14 14:26:20 +0200257 ly_print_(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200258 }
259
Michal Vasko52927e22020-03-16 17:26:14 +0100260 return LY_SUCCESS;
261}
262
263static LY_ERR
264xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
265{
266 /* print node name */
Michal Vasko5233e962020-08-14 14:26:20 +0200267 ly_print_(ctx->out, "%*s<%s", INDENT, node->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100268
269 /* print default namespace */
Radek Krejci1798aae2020-07-14 13:26:06 +0200270 xml_print_ns_opaq(ctx, node->format, &node->prefix, LYXML_PREFIX_DEFAULT);
Radek Krejcie7b95092019-05-15 11:03:07 +0200271
Michal Vasko52927e22020-03-16 17:26:14 +0100272 /* print attributes */
273 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200274
275 return LY_SUCCESS;
276}
277
278static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
279
280/**
281 * @brief Print XML element representing lyd_node_term.
282 *
283 * @param[in] ctx XML printer context.
284 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200285 */
Michal Vasko52927e22020-03-16 17:26:14 +0100286static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200287xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
288{
Radek Krejcia1911222019-07-22 17:24:50 +0200289 struct ly_set ns_list = {0};
Radek Krejci857189e2020-09-01 13:26:36 +0200290 ly_bool dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200291 const char *value;
292
Michal Vasko52927e22020-03-16 17:26:14 +0100293 xml_print_node_open(ctx, (struct lyd_node *)node);
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200294 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 +0200295
Radek Krejcia1911222019-07-22 17:24:50 +0200296 /* print namespaces connected with the values's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200297 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100298 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Michal Vasko5233e962020-08-14 14:26:20 +0200299 ly_print_(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200300 }
Radek Krejcia1911222019-07-22 17:24:50 +0200301 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200302
Radek Krejcia1911222019-07-22 17:24:50 +0200303 if (!value || !value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200304 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200305 } else {
Michal Vasko5233e962020-08-14 14:26:20 +0200306 ly_print_(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200307 lyxml_dump_text(ctx->out, value, 0);
Michal Vasko5233e962020-08-14 14:26:20 +0200308 ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200309 }
Radek Krejcia1911222019-07-22 17:24:50 +0200310 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100311 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200312 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200313}
314
315/**
316 * @brief Print XML element representing lyd_node_inner.
317 *
318 * @param[in] ctx XML printer context.
319 * @param[in] node Data node to be printed.
320 * @return LY_ERR value.
321 */
322static LY_ERR
323xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
324{
325 LY_ERR ret;
326 struct lyd_node *child;
327
Michal Vasko52927e22020-03-16 17:26:14 +0100328 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200329
330 if (!node->child) {
Michal Vasko5233e962020-08-14 14:26:20 +0200331 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200332 return LY_SUCCESS;
333 }
334
335 /* children */
Michal Vasko5233e962020-08-14 14:26:20 +0200336 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200337
338 LEVEL_INC;
339 LY_LIST_FOR(node->child, child) {
340 ret = xml_print_node(ctx, child);
341 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
342 }
343 LEVEL_DEC;
344
Michal Vasko5233e962020-08-14 14:26:20 +0200345 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200346
347 return LY_SUCCESS;
348}
349
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200350static LY_ERR
351xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200352{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200353 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200354 struct lyd_node *iter;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200355 uint32_t prev_opts, prev_lo;
Michal Vasko52927e22020-03-16 17:26:14 +0100356 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200357
Michal Vasko52927e22020-03-16 17:26:14 +0100358 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200359
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200360 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200361 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200362no_content:
Michal Vasko5233e962020-08-14 14:26:20 +0200363 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200364 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200365 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200366 if (any->value_type == LYD_ANYDATA_LYB) {
367 /* turn logging off */
368 prev_lo = ly_log_options(0);
369
370 /* try to parse it into a data tree */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200371 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 +0200372 /* successfully parsed */
373 free(any->value.mem);
374 any->value.tree = iter;
375 any->value_type = LYD_ANYDATA_DATATREE;
376 }
Radek Krejci7931b192020-06-25 17:05:03 +0200377
378 /* turn loggin on again */
379 ly_log_options(prev_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200380 }
381
Radek Krejcie7b95092019-05-15 11:03:07 +0200382 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200383 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200384 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100385 prev_opts = ctx->options;
Radek Krejci7931b192020-06-25 17:05:03 +0200386 ctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200387 LEVEL_INC;
388
Michal Vasko5233e962020-08-14 14:26:20 +0200389 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200390 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100391 ret = xml_print_node(ctx, iter);
392 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200393 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200394
395 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100396 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200397 break;
398 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200399 /* escape XML-sensitive characters */
400 if (!any->value.str[0]) {
401 goto no_content;
402 }
403 /* close opening tag and print data */
Michal Vasko5233e962020-08-14 14:26:20 +0200404 ly_print_(ctx->out, ">");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200405 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200406 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200407 case LYD_ANYDATA_XML:
408 /* print without escaping special characters */
409 if (!any->value.str[0]) {
410 goto no_content;
411 }
Michal Vasko5233e962020-08-14 14:26:20 +0200412 ly_print_(ctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200413 break;
414 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200415 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200416 /* JSON and LYB format is not supported */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200417 LOGWRN(LYD_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200418 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200419 }
420
421 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200422 if (any->value_type == LYD_ANYDATA_DATATREE) {
Michal Vasko5233e962020-08-14 14:26:20 +0200423 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200424 } else {
Michal Vasko5233e962020-08-14 14:26:20 +0200425 ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200426 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200427 }
428
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200429 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200430}
Radek Krejcie7b95092019-05-15 11:03:07 +0200431
Michal Vasko52927e22020-03-16 17:26:14 +0100432static LY_ERR
433xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
434{
435 LY_ERR ret;
436 struct lyd_node *child;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200437 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100438
439 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
440
441 if (node->value[0]) {
442 /* print namespaces connected with the value's prefixes */
443 if (node->val_prefs) {
444 LY_ARRAY_FOR(node->val_prefs, u) {
Radek Krejci1798aae2020-07-14 13:26:06 +0200445 xml_print_ns_opaq(ctx, node->format, &node->val_prefs[u], LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100446 }
447 }
448
Michal Vasko5233e962020-08-14 14:26:20 +0200449 ly_print_(ctx->out, ">%s", node->value);
Michal Vasko52927e22020-03-16 17:26:14 +0100450 }
451
452 if (node->child) {
453 /* children */
454 if (!node->value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200455 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100456 }
457
458 LEVEL_INC;
459 LY_LIST_FOR(node->child, child) {
460 ret = xml_print_node(ctx, child);
461 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
462 }
463 LEVEL_DEC;
464
Michal Vasko5233e962020-08-14 14:26:20 +0200465 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100466 } else if (node->value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200467 ly_print_(ctx->out, "</%s>%s", node->name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100468 } else {
469 /* no value or children */
Michal Vasko5233e962020-08-14 14:26:20 +0200470 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100471 }
472
473 return LY_SUCCESS;
474}
475
Radek Krejcie7b95092019-05-15 11:03:07 +0200476/**
477 * @brief Print XML element representing lyd_node.
478 *
479 * @param[in] ctx XML printer context.
480 * @param[in] node Data node to be printed.
481 * @return LY_ERR value.
482 */
483static LY_ERR
484xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
485{
486 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100487 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200488
Michal Vasko9b368d32020-02-14 13:53:31 +0100489 if (!ly_should_print(node, ctx->options)) {
490 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100491 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200492 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200493
Michal Vasko52927e22020-03-16 17:26:14 +0100494 /* remember namespace definition count on this level */
495 ns_count = ctx->ns.count;
496
497 if (!node->schema) {
498 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
499 } else {
500 switch (node->schema->nodetype) {
501 case LYS_CONTAINER:
502 case LYS_LIST:
503 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100504 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100505 case LYS_ACTION:
506 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
507 break;
508 case LYS_LEAF:
509 case LYS_LEAFLIST:
510 xml_print_term(ctx, (const struct lyd_node_term *)node);
511 break;
512 case LYS_ANYXML:
513 case LYS_ANYDATA:
514 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
515 break;
516 default:
517 LOGINT(node->schema->module->ctx);
518 ret = LY_EINT;
519 break;
520 }
521 }
522
523 /* remove all added namespaces */
524 while (ns_count < ctx->ns.count) {
525 FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
526 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
527 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200528 }
529
530 return ret;
531}
532
533LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200534xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200535{
536 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100537 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200538
539 if (!root) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200540 if (out->type == LY_OUT_MEMORY || out->type == LY_OUT_CALLBACK) {
Michal Vasko5233e962020-08-14 14:26:20 +0200541 ly_print_(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200542 }
543 goto finish;
544 }
545
Michal Vasko52927e22020-03-16 17:26:14 +0100546 ctx.out = out;
Radek Krejci52f65552020-09-01 17:03:35 +0200547 ctx.level = (options & LYD_PRINT_SHRINK ? 0 : 1);
Michal Vasko52927e22020-03-16 17:26:14 +0100548 ctx.options = options;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200549 ctx.ctx = LYD_CTX(root);
Michal Vasko52927e22020-03-16 17:26:14 +0100550
Radek Krejcie7b95092019-05-15 11:03:07 +0200551 /* content */
552 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100553 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejci7931b192020-06-25 17:05:03 +0200554 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200555 break;
556 }
557 }
558
559finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100560 assert(!ctx.prefix.count && !ctx.ns.count);
561 ly_set_erase(&ctx.prefix, NULL);
562 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200563 ly_print_flush(out);
564 return LY_SUCCESS;
565}