blob: 31c5ad4ca649e4ccc14ca156a8b9be5645b7ac59 [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 *
Michal Vasko78041d12022-11-29 14:11:07 +01007 * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
Radek Krejcie7b95092019-05-15 11:03:07 +02008 *
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 "context.h"
22#include "dict.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020023#include "log.h"
Michal Vasko8f702ee2024-02-20 15:44:24 +010024#include "ly_common.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"
Michal Vaskob4750962022-10-06 15:33:35 +020028#include "plugins_exts/metadata.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020029#include "plugins_types.h"
30#include "printer_data.h"
31#include "printer_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020032#include "set.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020033#include "tree.h"
Radek Krejci47fab892020-11-05 17:02:41 +010034#include "tree_data.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020035#include "tree_schema.h"
36#include "xml.h"
37
38/**
39 * @brief XML printer context.
40 */
41struct xmlpr_ctx {
Radek Krejci1deb5be2020-08-26 16:43:36 +020042 struct ly_out *out; /**< output specification */
43 uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
44 uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */
Michal Vasko52927e22020-03-16 17:26:14 +010045 const struct ly_ctx *ctx; /**< libyang context */
Radek Krejci1deb5be2020-08-26 16:43:36 +020046 struct ly_set prefix; /**< printed namespace prefixes */
47 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020048};
49
Michal Vasko52927e22020-03-16 17:26:14 +010050#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejci1798aae2020-07-14 13:26:06 +020051#define LYXML_PREFIX_DEFAULT 0x02 /**< The namespace is required to be a default (without prefix) */
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 *
Michal Vasko61ad1ff2022-02-10 15:48:39 +010063xml_print_ns(struct xmlpr_ctx *pctx, const char *ns, const char *new_prefix, uint32_t prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020064{
Radek Krejciba03a5a2020-08-27 14:40:41 +020065 uint32_t i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010066
Michal Vasko61ad1ff2022-02-10 15:48:39 +010067 for (i = pctx->ns.count; i > 0; --i) {
Michal Vasko52927e22020-03-16 17:26:14 +010068 if (!new_prefix) {
69 /* find default namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010070 if (!pctx->prefix.objs[i - 1]) {
71 if (!strcmp(pctx->ns.objs[i - 1], ns)) {
Radek Krejciba03a5a2020-08-27 14:40:41 +020072 /* matching default namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010073 return pctx->prefix.objs[i - 1];
Radek Krejcie7b95092019-05-15 11:03:07 +020074 }
Radek Krejciba03a5a2020-08-27 14:40:41 +020075 /* not matching default namespace */
Michal Vasko52927e22020-03-16 17:26:14 +010076 break;
77 }
78 } else {
79 /* find prefixed namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010080 if (!strcmp(pctx->ns.objs[i - 1], ns)) {
81 if (!pctx->prefix.objs[i - 1]) {
Michal Vasko52927e22020-03-16 17:26:14 +010082 /* default namespace is not interesting */
83 continue;
84 }
85
Michal Vasko61ad1ff2022-02-10 15:48:39 +010086 if (!strcmp(pctx->prefix.objs[i - 1], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
Michal Vasko52927e22020-03-16 17:26:14 +010087 /* the same prefix or can be any */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010088 return pctx->prefix.objs[i - 1];
Michal Vasko52927e22020-03-16 17:26:14 +010089 }
90 }
Radek Krejcie7b95092019-05-15 11:03:07 +020091 }
Radek Krejcie7b95092019-05-15 11:03:07 +020092 }
Radek Krejci28681fa2019-09-06 13:08:45 +020093
Radek Krejciba03a5a2020-08-27 14:40:41 +020094 /* suitable namespace not found, must be printed */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010095 ly_print_(pctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020096
Radek Krejciba03a5a2020-08-27 14:40:41 +020097 /* and added into namespaces */
98 if (new_prefix) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +010099 LY_CHECK_RET(lydict_insert(pctx->ctx, new_prefix, 0, &new_prefix), NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200100 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100101 LY_CHECK_RET(ly_set_add(&pctx->prefix, (void *)new_prefix, 1, NULL), NULL);
102 LY_CHECK_RET(ly_set_add(&pctx->ns, (void *)ns, 1, &i), NULL);
Michal Vasko52927e22020-03-16 17:26:14 +0100103
104 /* return it */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100105 return pctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200106}
107
Michal Vasko22df3f02020-08-24 13:29:22 +0200108static const char *
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100109xml_print_ns_opaq(struct xmlpr_ctx *pctx, LY_VALUE_FORMAT format, const struct ly_opaq_name *name, uint32_t prefix_opts)
Radek Krejci1798aae2020-07-14 13:26:06 +0200110{
Radek Krejci1798aae2020-07-14 13:26:06 +0200111 switch (format) {
Radek Krejci8df109d2021-04-23 12:19:08 +0200112 case LY_VALUE_XML:
Michal Vasko9c8125e2023-09-08 08:08:25 +0200113 if (name->module_ns) {
114 return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
115 }
Radek Krejci1798aae2020-07-14 13:26:06 +0200116 break;
Radek Krejci8df109d2021-04-23 12:19:08 +0200117 case LY_VALUE_JSON:
Michal Vaskoad92b672020-11-12 13:11:31 +0100118 if (name->module_name) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100119 const struct lys_module *mod = ly_ctx_get_module_latest(pctx->ctx, name->module_name);
Michal Vasko26bbb272022-08-02 14:54:33 +0200120
Radek Krejci1798aae2020-07-14 13:26:06 +0200121 if (mod) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100122 return xml_print_ns(pctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
Radek Krejci1798aae2020-07-14 13:26:06 +0200123 }
124 }
125 break;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100126 default:
Radek Krejci1798aae2020-07-14 13:26:06 +0200127 /* cannot be created */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100128 LOGINT(pctx->ctx);
Radek Krejci1798aae2020-07-14 13:26:06 +0200129 }
130
131 return NULL;
132}
133
Radek Krejciec9ad602021-01-04 10:46:30 +0100134/**
135 * @brief Print prefix data.
136 *
137 * @param[in] ctx XML printer context.
Radek Krejci8df109d2021-04-23 12:19:08 +0200138 * @param[in] format Value prefix format, only ::LY_VALUE_XML supported.
139 * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
Radek Krejciec9ad602021-01-04 10:46:30 +0100140 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
141 * @return LY_ERR value.
142 */
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100143static void
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100144xml_print_ns_prefix_data(struct xmlpr_ctx *pctx, LY_VALUE_FORMAT format, void *prefix_data, uint32_t prefix_opts)
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100145{
146 const struct ly_set *set;
147 const struct lyxml_ns *ns;
148 uint32_t i;
149
150 switch (format) {
Radek Krejci8df109d2021-04-23 12:19:08 +0200151 case LY_VALUE_XML:
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100152 set = prefix_data;
153 for (i = 0; i < set->count; ++i) {
154 ns = set->objs[i];
Michal Vasko294e7f02022-02-28 13:59:00 +0100155 if (!ns->prefix) {
156 /* default namespace is not for the element */
157 continue;
158 }
159
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100160 xml_print_ns(pctx, ns->uri, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : ns->prefix, prefix_opts);
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100161 }
162 break;
163 default:
164 /* cannot be created */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100165 LOGINT(pctx->ctx);
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100166 }
167}
168
Radek Krejci28681fa2019-09-06 13:08:45 +0200169/**
Michal Vasko4da82072022-05-13 11:13:49 +0200170 * @brief Print metadata of a node.
171 *
172 * @param[in] pctx XML printer context.
173 * @param[in] node Data node with metadata.
Radek Krejcie7b95092019-05-15 11:03:07 +0200174 */
Michal Vasko52927e22020-03-16 17:26:14 +0100175static void
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100176xml_print_meta(struct xmlpr_ctx *pctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200177{
Michal Vasko9f96a052020-03-10 09:41:45 +0100178 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100179 const struct lys_module *mod;
180 struct ly_set ns_list = {0};
Michal Vasko45791ad2021-06-17 08:45:03 +0200181 LY_ARRAY_COUNT_TYPE u;
182 ly_bool dynamic, filter_attrs = 0;
Michal Vasko32f067f2023-06-01 12:31:14 +0200183 const char *value;
184 uint32_t i;
Radek Krejcie7b95092019-05-15 11:03:07 +0200185
Radek Krejcie7b95092019-05-15 11:03:07 +0200186 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100187 if (node->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100188 if (((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
189 ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100190 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
Radek Krejcic2b49d82021-04-26 08:05:57 +0200191 mod = ly_ctx_get_module_latest(LYD_CTX(node), "ietf-netconf-with-defaults");
Michal Vasko52927e22020-03-16 17:26:14 +0100192 if (mod) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100193 ly_print_(pctx->out, " %s:default=\"true\"", xml_print_ns(pctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200194 }
195 }
196 }
Michal Vasko45791ad2021-06-17 08:45:03 +0200197
198 /* check for NETCONF filter unqualified attributes */
Michal Vasko1b2a3f42022-12-20 09:38:28 +0100199 if (!strcmp(node->schema->module->name, "notifications")) {
200 filter_attrs = 1;
201 } else {
202 LY_ARRAY_FOR(node->schema->exts, u) {
203 if (!strcmp(node->schema->exts[u].def->name, "get-filter-element-attributes") &&
204 !strcmp(node->schema->exts[u].def->module->name, "ietf-netconf")) {
205 filter_attrs = 1;
206 break;
207 }
Michal Vasko45791ad2021-06-17 08:45:03 +0200208 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200209 }
Michal Vasko45791ad2021-06-17 08:45:03 +0200210
Michal Vasko9f96a052020-03-10 09:41:45 +0100211 for (meta = node->meta; meta; meta = meta->next) {
aPiecek6cf1d162023-11-08 16:07:00 +0100212 if (!lyd_metadata_should_print(meta)) {
213 continue;
214 }
215
Michal Vasko32f067f2023-06-01 12:31:14 +0200216 /* store the module of the default namespace, NULL because there is none */
217 ly_set_add(&ns_list, NULL, 0, NULL);
218
219 /* print the value */
220 value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list, &dynamic, NULL);
Radek Krejci28681fa2019-09-06 13:08:45 +0200221
Michal Vasko52927e22020-03-16 17:26:14 +0100222 /* print namespaces connected with the value's prefixes */
Michal Vasko32f067f2023-06-01 12:31:14 +0200223 for (i = 1; i < ns_list.count; ++i) {
Michal Vasko45791ad2021-06-17 08:45:03 +0200224 mod = ns_list.objs[i];
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100225 xml_print_ns(pctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200226 }
227 ly_set_erase(&ns_list, NULL);
228
Radek Krejci0f969882020-08-21 16:56:47 +0200229 mod = meta->annotation->module;
Michal Vasko45791ad2021-06-17 08:45:03 +0200230 if (filter_attrs && !strcmp(mod->name, "ietf-netconf") && (!strcmp(meta->name, "type") ||
231 !strcmp(meta->name, "select"))) {
232 /* print special NETCONF filter unqualified attributes */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100233 ly_print_(pctx->out, " %s=\"", meta->name);
Michal Vasko45791ad2021-06-17 08:45:03 +0200234 } else {
235 /* print the metadata with its namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100236 ly_print_(pctx->out, " %s:%s=\"", xml_print_ns(pctx, mod->ns, mod->prefix, 1), meta->name);
Michal Vasko45791ad2021-06-17 08:45:03 +0200237 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200238
Michal Vasko52927e22020-03-16 17:26:14 +0100239 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200240 if (value && value[0]) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100241 lyxml_dump_text(pctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200242 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100243 ly_print_(pctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200244 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100245 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200246 }
247 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200248}
249
250/**
251 * @brief Print generic XML element despite of the data node type.
252 *
253 * Prints the element name, attributes and necessary namespaces.
254 *
255 * @param[in] ctx XML printer context.
256 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200257 */
Michal Vasko52927e22020-03-16 17:26:14 +0100258static void
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100259xml_print_node_open(struct xmlpr_ctx *pctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200260{
Michal Vasko52927e22020-03-16 17:26:14 +0100261 /* print node name */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100262 ly_print_(pctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100263
264 /* print default namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100265 xml_print_ns(pctx, node->schema->module->ns, NULL, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100266
267 /* print metadata */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100268 xml_print_meta(pctx, node);
Michal Vasko52927e22020-03-16 17:26:14 +0100269}
270
271static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100272xml_print_attr(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
Michal Vasko52927e22020-03-16 17:26:14 +0100273{
Radek Krejci5536d282020-08-04 23:27:44 +0200274 const struct lyd_attr *attr;
Michal Vasko52927e22020-03-16 17:26:14 +0100275 const char *pref;
Michal Vasko52927e22020-03-16 17:26:14 +0100276
277 LY_LIST_FOR(node->attr, attr) {
278 pref = NULL;
Michal Vaskoad92b672020-11-12 13:11:31 +0100279 if (attr->name.prefix) {
Michal Vasko52927e22020-03-16 17:26:14 +0100280 /* print attribute namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100281 pref = xml_print_ns_opaq(pctx, attr->format, &attr->name, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100282 }
283
284 /* print namespaces connected with the value's prefixes */
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100285 if (attr->val_prefix_data) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100286 xml_print_ns_prefix_data(pctx, attr->format, attr->val_prefix_data, LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100287 }
288
289 /* print the attribute with its prefix and value */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100290 ly_print_(pctx->out, " %s%s%s=\"", pref ? pref : "", pref ? ":" : "", attr->name.name);
291 lyxml_dump_text(pctx->out, attr->value, 1);
292 ly_print_(pctx->out, "\""); /* print attribute value terminator */
Radek IÅ¡a52c4ac62021-03-08 09:37:32 +0100293
Radek Krejcie7b95092019-05-15 11:03:07 +0200294 }
295
Michal Vasko52927e22020-03-16 17:26:14 +0100296 return LY_SUCCESS;
297}
298
299static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100300xml_print_opaq_open(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
Michal Vasko52927e22020-03-16 17:26:14 +0100301{
302 /* print node name */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100303 ly_print_(pctx->out, "%*s<%s", INDENT, node->name.name);
Michal Vasko52927e22020-03-16 17:26:14 +0100304
Michal Vaskoea0f96c2022-12-20 08:34:39 +0100305 if (node->name.prefix || node->name.module_ns) {
306 /* print default namespace */
307 xml_print_ns_opaq(pctx, node->format, &node->name, LYXML_PREFIX_DEFAULT);
308 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200309
Michal Vasko52927e22020-03-16 17:26:14 +0100310 /* print attributes */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100311 LY_CHECK_RET(xml_print_attr(pctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200312
313 return LY_SUCCESS;
314}
315
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100316static LY_ERR xml_print_node(struct xmlpr_ctx *pctx, const struct lyd_node *node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200317
318/**
319 * @brief Print XML element representing lyd_node_term.
320 *
321 * @param[in] ctx XML printer context.
322 * @param[in] node Data node to be printed.
Michal Vasko569f2f42021-08-04 11:31:08 +0200323 * @return LY_ERR value.
Radek Krejcie7b95092019-05-15 11:03:07 +0200324 */
Michal Vasko569f2f42021-08-04 11:31:08 +0200325static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100326xml_print_term(struct xmlpr_ctx *pctx, const struct lyd_node_term *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200327{
Michal Vasko32f067f2023-06-01 12:31:14 +0200328 LY_ERR rc = LY_SUCCESS;
Radek Krejcia1911222019-07-22 17:24:50 +0200329 struct ly_set ns_list = {0};
Michal Vasko32f067f2023-06-01 12:31:14 +0200330 ly_bool dynamic = 0;
331 const char *value = NULL;
332 const struct lys_module *mod;
333 uint32_t i;
Radek Krejci28681fa2019-09-06 13:08:45 +0200334
Michal Vasko32f067f2023-06-01 12:31:14 +0200335 /* store the module of the default namespace */
336 if ((rc = ly_set_add(&ns_list, node->schema->module, 0, NULL))) {
337 LOGMEM(pctx->ctx);
338 goto cleanup;
339 }
340
341 /* print the value */
Radek Krejci224d4b42021-04-23 13:54:59 +0200342 value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML,
343 &ns_list, &dynamic, NULL);
Michal Vasko32f067f2023-06-01 12:31:14 +0200344 LY_CHECK_ERR_GOTO(!value, rc = LY_EINVAL, cleanup);
345
346 /* print node opening */
347 xml_print_node_open(pctx, &node->node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200348
Radek Krejcia1911222019-07-22 17:24:50 +0200349 /* print namespaces connected with the values's prefixes */
Michal Vasko32f067f2023-06-01 12:31:14 +0200350 for (i = 1; i < ns_list.count; ++i) {
351 mod = ns_list.objs[i];
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100352 ly_print_(pctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200353 }
354
Michal Vasko569f2f42021-08-04 11:31:08 +0200355 if (!value[0]) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100356 ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200357 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100358 ly_print_(pctx->out, ">");
359 lyxml_dump_text(pctx->out, value, 0);
360 ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200361 }
Michal Vasko32f067f2023-06-01 12:31:14 +0200362
363cleanup:
364 ly_set_erase(&ns_list, NULL);
Radek Krejcia1911222019-07-22 17:24:50 +0200365 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100366 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200367 }
Michal Vasko32f067f2023-06-01 12:31:14 +0200368 return rc;
Radek Krejcie7b95092019-05-15 11:03:07 +0200369}
370
371/**
372 * @brief Print XML element representing lyd_node_inner.
373 *
374 * @param[in] ctx XML printer context.
375 * @param[in] node Data node to be printed.
376 * @return LY_ERR value.
377 */
378static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100379xml_print_inner(struct xmlpr_ctx *pctx, const struct lyd_node_inner *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200380{
381 LY_ERR ret;
382 struct lyd_node *child;
383
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100384 xml_print_node_open(pctx, &node->node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200385
Michal Vasko630d9892020-12-08 17:11:08 +0100386 LY_LIST_FOR(node->child, child) {
Michal Vasko8db584d2022-03-30 13:42:48 +0200387 if (lyd_node_should_print(child, pctx->options)) {
Michal Vasko630d9892020-12-08 17:11:08 +0100388 break;
389 }
390 }
391 if (!child) {
392 /* there are no children that will be printed */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100393 ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200394 return LY_SUCCESS;
395 }
396
397 /* children */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100398 ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200399
400 LEVEL_INC;
401 LY_LIST_FOR(node->child, child) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100402 ret = xml_print_node(pctx, child);
Radek Krejcie7b95092019-05-15 11:03:07 +0200403 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
404 }
405 LEVEL_DEC;
406
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100407 ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200408
409 return LY_SUCCESS;
410}
411
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200412static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100413xml_print_anydata(struct xmlpr_ctx *pctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200414{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200415 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200416 struct lyd_node *iter;
Michal Vasko59004e32024-01-30 16:09:54 +0100417 uint32_t prev_opts, *prev_lo, temp_lo = 0;
Michal Vasko52927e22020-03-16 17:26:14 +0100418 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200419
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +0100420 if ((node->schema->nodetype == LYS_ANYDATA) && (node->value_type != LYD_ANYDATA_DATATREE)) {
421 LOGINT_RET(pctx->ctx);
422 }
423
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100424 xml_print_node_open(pctx, &node->node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200425
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200426 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200427 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200428no_content:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100429 ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200430 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200431 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200432 if (any->value_type == LYD_ANYDATA_LYB) {
433 /* turn logging off */
Michal Vasko59004e32024-01-30 16:09:54 +0100434 prev_lo = ly_temp_log_options(&temp_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200435
436 /* try to parse it into a data tree */
Michal Vasko78041d12022-11-29 14:11:07 +0100437 if (lyd_parse_data_mem((struct ly_ctx *)LYD_CTX(node), any->value.mem, LYD_LYB,
438 LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0, &iter) == LY_SUCCESS) {
Michal Vasko60ea6352020-06-29 13:39:39 +0200439 /* successfully parsed */
440 free(any->value.mem);
441 any->value.tree = iter;
442 any->value_type = LYD_ANYDATA_DATATREE;
443 }
Radek Krejci7931b192020-06-25 17:05:03 +0200444
Michal Vasko78041d12022-11-29 14:11:07 +0100445 /* turn logging on again */
Michal Vasko59004e32024-01-30 16:09:54 +0100446 ly_temp_log_options(prev_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200447 }
448
Radek Krejcie7b95092019-05-15 11:03:07 +0200449 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200450 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200451 /* close opening tag and print data */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100452 prev_opts = pctx->options;
453 pctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200454 LEVEL_INC;
455
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100456 ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200457 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100458 ret = xml_print_node(pctx, iter);
Michal Vasko52927e22020-03-16 17:26:14 +0100459 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200460 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200461
462 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100463 pctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200464 break;
465 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200466 /* escape XML-sensitive characters */
467 if (!any->value.str[0]) {
468 goto no_content;
469 }
470 /* close opening tag and print data */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100471 ly_print_(pctx->out, ">");
472 lyxml_dump_text(pctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200473 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200474 case LYD_ANYDATA_XML:
475 /* print without escaping special characters */
476 if (!any->value.str[0]) {
477 goto no_content;
478 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100479 ly_print_(pctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200480 break;
481 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200482 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200483 /* JSON and LYB format is not supported */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100484 LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200485 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200486 }
487
488 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200489 if (any->value_type == LYD_ANYDATA_DATATREE) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100490 ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200491 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100492 ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200493 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200494 }
495
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200496 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200497}
Radek Krejcie7b95092019-05-15 11:03:07 +0200498
Michal Vasko52927e22020-03-16 17:26:14 +0100499static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100500xml_print_opaq(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
Michal Vasko52927e22020-03-16 17:26:14 +0100501{
502 LY_ERR ret;
503 struct lyd_node *child;
Michal Vasko52927e22020-03-16 17:26:14 +0100504
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100505 LY_CHECK_RET(xml_print_opaq_open(pctx, node));
Michal Vasko52927e22020-03-16 17:26:14 +0100506
507 if (node->value[0]) {
508 /* print namespaces connected with the value's prefixes */
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100509 if (node->val_prefix_data) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100510 xml_print_ns_prefix_data(pctx, node->format, node->val_prefix_data, LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100511 }
512
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100513 ly_print_(pctx->out, ">");
514 lyxml_dump_text(pctx->out, node->value, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100515 }
516
517 if (node->child) {
518 /* children */
519 if (!node->value[0]) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100520 ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100521 }
522
523 LEVEL_INC;
524 LY_LIST_FOR(node->child, child) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100525 ret = xml_print_node(pctx, child);
Michal Vasko52927e22020-03-16 17:26:14 +0100526 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
527 }
528 LEVEL_DEC;
529
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100530 ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->name.name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100531 } else if (node->value[0]) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100532 ly_print_(pctx->out, "</%s>%s", node->name.name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100533 } else {
534 /* no value or children */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100535 ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100536 }
537
538 return LY_SUCCESS;
539}
540
Radek Krejcie7b95092019-05-15 11:03:07 +0200541/**
542 * @brief Print XML element representing lyd_node.
543 *
544 * @param[in] ctx XML printer context.
545 * @param[in] node Data node to be printed.
546 * @return LY_ERR value.
547 */
548static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100549xml_print_node(struct xmlpr_ctx *pctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200550{
551 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100552 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200553
Michal Vasko8db584d2022-03-30 13:42:48 +0200554 if (!lyd_node_should_print(node, pctx->options)) {
Michal Vasko9b368d32020-02-14 13:53:31 +0100555 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100556 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200557 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200558
Michal Vasko52927e22020-03-16 17:26:14 +0100559 /* remember namespace definition count on this level */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100560 ns_count = pctx->ns.count;
Michal Vasko52927e22020-03-16 17:26:14 +0100561
562 if (!node->schema) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100563 ret = xml_print_opaq(pctx, (const struct lyd_node_opaq *)node);
Michal Vasko52927e22020-03-16 17:26:14 +0100564 } else {
565 switch (node->schema->nodetype) {
566 case LYS_CONTAINER:
567 case LYS_LIST:
568 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100569 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100570 case LYS_ACTION:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100571 ret = xml_print_inner(pctx, (const struct lyd_node_inner *)node);
Michal Vasko52927e22020-03-16 17:26:14 +0100572 break;
573 case LYS_LEAF:
574 case LYS_LEAFLIST:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100575 ret = xml_print_term(pctx, (const struct lyd_node_term *)node);
Michal Vasko52927e22020-03-16 17:26:14 +0100576 break;
577 case LYS_ANYXML:
578 case LYS_ANYDATA:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100579 ret = xml_print_anydata(pctx, (const struct lyd_node_any *)node);
Michal Vasko52927e22020-03-16 17:26:14 +0100580 break;
581 default:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100582 LOGINT(pctx->ctx);
Michal Vasko52927e22020-03-16 17:26:14 +0100583 ret = LY_EINT;
584 break;
585 }
586 }
587
588 /* remove all added namespaces */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100589 while (ns_count < pctx->ns.count) {
590 lydict_remove(pctx->ctx, pctx->prefix.objs[pctx->prefix.count - 1]);
591 ly_set_rm_index(&pctx->prefix, pctx->prefix.count - 1, NULL);
592 ly_set_rm_index(&pctx->ns, pctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200593 }
594
595 return ret;
596}
597
598LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200599xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200600{
601 const struct lyd_node *node;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100602 struct xmlpr_ctx pctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200603
604 if (!root) {
Michal Vasko69730152020-10-09 16:30:07 +0200605 if ((out->type == LY_OUT_MEMORY) || (out->type == LY_OUT_CALLBACK)) {
Michal Vasko5233e962020-08-14 14:26:20 +0200606 ly_print_(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200607 }
608 goto finish;
609 }
610
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100611 pctx.out = out;
612 pctx.level = 0;
613 pctx.options = options;
614 pctx.ctx = LYD_CTX(root);
Michal Vasko52927e22020-03-16 17:26:14 +0100615
Radek Krejcie7b95092019-05-15 11:03:07 +0200616 /* content */
617 LY_LIST_FOR(root, node) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100618 LY_CHECK_RET(xml_print_node(&pctx, node));
Radek Krejci7931b192020-06-25 17:05:03 +0200619 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200620 break;
621 }
622 }
623
624finish:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100625 assert(!pctx.prefix.count && !pctx.ns.count);
626 ly_set_erase(&pctx.prefix, NULL);
627 ly_set_erase(&pctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200628 ly_print_flush(out);
629 return LY_SUCCESS;
630}