blob: 267fb093fe9f76bcd8be629c37d6a0ba1b4c102b [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 *
Michal Vasko61ad1ff2022-02-10 15:48:39 +010062xml_print_ns(struct xmlpr_ctx *pctx, 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
Michal Vasko61ad1ff2022-02-10 15:48:39 +010066 for (i = pctx->ns.count; i > 0; --i) {
Michal Vasko52927e22020-03-16 17:26:14 +010067 if (!new_prefix) {
68 /* find default namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010069 if (!pctx->prefix.objs[i - 1]) {
70 if (!strcmp(pctx->ns.objs[i - 1], ns)) {
Radek Krejciba03a5a2020-08-27 14:40:41 +020071 /* matching default namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010072 return pctx->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 */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010079 if (!strcmp(pctx->ns.objs[i - 1], ns)) {
80 if (!pctx->prefix.objs[i - 1]) {
Michal Vasko52927e22020-03-16 17:26:14 +010081 /* default namespace is not interesting */
82 continue;
83 }
84
Michal Vasko61ad1ff2022-02-10 15:48:39 +010085 if (!strcmp(pctx->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 */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010087 return pctx->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 */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010094 ly_print_(pctx->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) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +010098 LY_CHECK_RET(lydict_insert(pctx->ctx, new_prefix, 0, &new_prefix), NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +020099 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100100 LY_CHECK_RET(ly_set_add(&pctx->prefix, (void *)new_prefix, 1, NULL), NULL);
101 LY_CHECK_RET(ly_set_add(&pctx->ns, (void *)ns, 1, &i), NULL);
Michal Vasko52927e22020-03-16 17:26:14 +0100102
103 /* return it */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100104 return pctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200105}
106
Michal Vasko22df3f02020-08-24 13:29:22 +0200107static const char *
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100108xml_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 +0200109{
Radek Krejci1798aae2020-07-14 13:26:06 +0200110 switch (format) {
Radek Krejci8df109d2021-04-23 12:19:08 +0200111 case LY_VALUE_XML:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100112 return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
Radek Krejci1798aae2020-07-14 13:26:06 +0200113 break;
Radek Krejci8df109d2021-04-23 12:19:08 +0200114 case LY_VALUE_JSON:
Michal Vaskoad92b672020-11-12 13:11:31 +0100115 if (name->module_name) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100116 const struct lys_module *mod = ly_ctx_get_module_latest(pctx->ctx, name->module_name);
Michal Vasko26bbb272022-08-02 14:54:33 +0200117
Radek Krejci1798aae2020-07-14 13:26:06 +0200118 if (mod) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100119 return xml_print_ns(pctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
Radek Krejci1798aae2020-07-14 13:26:06 +0200120 }
121 }
122 break;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100123 default:
Radek Krejci1798aae2020-07-14 13:26:06 +0200124 /* cannot be created */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100125 LOGINT(pctx->ctx);
Radek Krejci1798aae2020-07-14 13:26:06 +0200126 }
127
128 return NULL;
129}
130
Radek Krejciec9ad602021-01-04 10:46:30 +0100131/**
132 * @brief Print prefix data.
133 *
134 * @param[in] ctx XML printer context.
Radek Krejci8df109d2021-04-23 12:19:08 +0200135 * @param[in] format Value prefix format, only ::LY_VALUE_XML supported.
136 * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
Radek Krejciec9ad602021-01-04 10:46:30 +0100137 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
138 * @return LY_ERR value.
139 */
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100140static void
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100141xml_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 +0100142{
143 const struct ly_set *set;
144 const struct lyxml_ns *ns;
145 uint32_t i;
146
147 switch (format) {
Radek Krejci8df109d2021-04-23 12:19:08 +0200148 case LY_VALUE_XML:
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100149 set = prefix_data;
150 for (i = 0; i < set->count; ++i) {
151 ns = set->objs[i];
Michal Vasko294e7f02022-02-28 13:59:00 +0100152 if (!ns->prefix) {
153 /* default namespace is not for the element */
154 continue;
155 }
156
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100157 xml_print_ns(pctx, ns->uri, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : ns->prefix, prefix_opts);
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100158 }
159 break;
160 default:
161 /* cannot be created */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100162 LOGINT(pctx->ctx);
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100163 }
164}
165
Radek Krejci28681fa2019-09-06 13:08:45 +0200166/**
Michal Vasko4da82072022-05-13 11:13:49 +0200167 * @brief Print metadata of a node.
168 *
169 * @param[in] pctx XML printer context.
170 * @param[in] node Data node with metadata.
Radek Krejcie7b95092019-05-15 11:03:07 +0200171 */
Michal Vasko52927e22020-03-16 17:26:14 +0100172static void
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100173xml_print_meta(struct xmlpr_ctx *pctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200174{
Michal Vasko9f96a052020-03-10 09:41:45 +0100175 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100176 const struct lys_module *mod;
177 struct ly_set ns_list = {0};
Michal Vasko45791ad2021-06-17 08:45:03 +0200178 LY_ARRAY_COUNT_TYPE u;
179 ly_bool dynamic, filter_attrs = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200180
Radek Krejcie7b95092019-05-15 11:03:07 +0200181 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100182 if (node->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100183 if (((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
184 ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100185 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
Radek Krejcic2b49d82021-04-26 08:05:57 +0200186 mod = ly_ctx_get_module_latest(LYD_CTX(node), "ietf-netconf-with-defaults");
Michal Vasko52927e22020-03-16 17:26:14 +0100187 if (mod) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100188 ly_print_(pctx->out, " %s:default=\"true\"", xml_print_ns(pctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200189 }
190 }
191 }
Michal Vasko45791ad2021-06-17 08:45:03 +0200192
193 /* check for NETCONF filter unqualified attributes */
194 LY_ARRAY_FOR(node->schema->exts, u) {
195 if (!strcmp(node->schema->exts[u].def->name, "get-filter-element-attributes") &&
196 !strcmp(node->schema->exts[u].def->module->name, "ietf-netconf")) {
197 filter_attrs = 1;
198 break;
199 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200200 }
Michal Vasko45791ad2021-06-17 08:45:03 +0200201
Michal Vasko9f96a052020-03-10 09:41:45 +0100202 for (meta = node->meta; meta; meta = meta->next) {
Radek Krejci224d4b42021-04-23 13:54:59 +0200203 const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list,
204 &dynamic, NULL);
Radek Krejci28681fa2019-09-06 13:08:45 +0200205
Michal Vasko52927e22020-03-16 17:26:14 +0100206 /* print namespaces connected with the value's prefixes */
Michal Vasko45791ad2021-06-17 08:45:03 +0200207 for (uint32_t i = 0; i < ns_list.count; ++i) {
208 mod = ns_list.objs[i];
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100209 xml_print_ns(pctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200210 }
211 ly_set_erase(&ns_list, NULL);
212
Radek Krejci0f969882020-08-21 16:56:47 +0200213 mod = meta->annotation->module;
Michal Vasko45791ad2021-06-17 08:45:03 +0200214 if (filter_attrs && !strcmp(mod->name, "ietf-netconf") && (!strcmp(meta->name, "type") ||
215 !strcmp(meta->name, "select"))) {
216 /* print special NETCONF filter unqualified attributes */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100217 ly_print_(pctx->out, " %s=\"", meta->name);
Michal Vasko45791ad2021-06-17 08:45:03 +0200218 } else {
219 /* print the metadata with its namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100220 ly_print_(pctx->out, " %s:%s=\"", xml_print_ns(pctx, mod->ns, mod->prefix, 1), meta->name);
Michal Vasko45791ad2021-06-17 08:45:03 +0200221 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200222
Michal Vasko52927e22020-03-16 17:26:14 +0100223 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200224 if (value && value[0]) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100225 lyxml_dump_text(pctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200226 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100227 ly_print_(pctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200228 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100229 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200230 }
231 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200232}
233
234/**
235 * @brief Print generic XML element despite of the data node type.
236 *
237 * Prints the element name, attributes and necessary namespaces.
238 *
239 * @param[in] ctx XML printer context.
240 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200241 */
Michal Vasko52927e22020-03-16 17:26:14 +0100242static void
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100243xml_print_node_open(struct xmlpr_ctx *pctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200244{
Michal Vasko52927e22020-03-16 17:26:14 +0100245 /* print node name */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100246 ly_print_(pctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100247
248 /* print default namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100249 xml_print_ns(pctx, node->schema->module->ns, NULL, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100250
251 /* print metadata */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100252 xml_print_meta(pctx, node);
Michal Vasko52927e22020-03-16 17:26:14 +0100253}
254
255static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100256xml_print_attr(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
Michal Vasko52927e22020-03-16 17:26:14 +0100257{
Radek Krejci5536d282020-08-04 23:27:44 +0200258 const struct lyd_attr *attr;
Michal Vasko52927e22020-03-16 17:26:14 +0100259 const char *pref;
Michal Vasko52927e22020-03-16 17:26:14 +0100260
261 LY_LIST_FOR(node->attr, attr) {
262 pref = NULL;
Michal Vaskoad92b672020-11-12 13:11:31 +0100263 if (attr->name.prefix) {
Michal Vasko52927e22020-03-16 17:26:14 +0100264 /* print attribute namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100265 pref = xml_print_ns_opaq(pctx, attr->format, &attr->name, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100266 }
267
268 /* print namespaces connected with the value's prefixes */
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100269 if (attr->val_prefix_data) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100270 xml_print_ns_prefix_data(pctx, attr->format, attr->val_prefix_data, LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100271 }
272
273 /* print the attribute with its prefix and value */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100274 ly_print_(pctx->out, " %s%s%s=\"", pref ? pref : "", pref ? ":" : "", attr->name.name);
275 lyxml_dump_text(pctx->out, attr->value, 1);
276 ly_print_(pctx->out, "\""); /* print attribute value terminator */
Radek IÅ¡a52c4ac62021-03-08 09:37:32 +0100277
Radek Krejcie7b95092019-05-15 11:03:07 +0200278 }
279
Michal Vasko52927e22020-03-16 17:26:14 +0100280 return LY_SUCCESS;
281}
282
283static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100284xml_print_opaq_open(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
Michal Vasko52927e22020-03-16 17:26:14 +0100285{
286 /* print node name */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100287 ly_print_(pctx->out, "%*s<%s", INDENT, node->name.name);
Michal Vasko52927e22020-03-16 17:26:14 +0100288
289 /* print default namespace */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100290 xml_print_ns_opaq(pctx, node->format, &node->name, LYXML_PREFIX_DEFAULT);
Radek Krejcie7b95092019-05-15 11:03:07 +0200291
Michal Vasko52927e22020-03-16 17:26:14 +0100292 /* print attributes */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100293 LY_CHECK_RET(xml_print_attr(pctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200294
295 return LY_SUCCESS;
296}
297
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100298static LY_ERR xml_print_node(struct xmlpr_ctx *pctx, const struct lyd_node *node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200299
300/**
301 * @brief Print XML element representing lyd_node_term.
302 *
303 * @param[in] ctx XML printer context.
304 * @param[in] node Data node to be printed.
Michal Vasko569f2f42021-08-04 11:31:08 +0200305 * @return LY_ERR value.
Radek Krejcie7b95092019-05-15 11:03:07 +0200306 */
Michal Vasko569f2f42021-08-04 11:31:08 +0200307static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100308xml_print_term(struct xmlpr_ctx *pctx, const struct lyd_node_term *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200309{
Radek Krejcia1911222019-07-22 17:24:50 +0200310 struct ly_set ns_list = {0};
Radek Krejci857189e2020-09-01 13:26:36 +0200311 ly_bool dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200312 const char *value;
313
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100314 xml_print_node_open(pctx, &node->node);
Radek Krejci224d4b42021-04-23 13:54:59 +0200315 value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML,
316 &ns_list, &dynamic, NULL);
Michal Vasko569f2f42021-08-04 11:31:08 +0200317 LY_CHECK_RET(!value, LY_EINVAL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200318
Radek Krejcia1911222019-07-22 17:24:50 +0200319 /* print namespaces connected with the values's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200320 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100321 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Michal Vasko26bbb272022-08-02 14:54:33 +0200322
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100323 ly_print_(pctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200324 }
Radek Krejcia1911222019-07-22 17:24:50 +0200325 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200326
Michal Vasko569f2f42021-08-04 11:31:08 +0200327 if (!value[0]) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100328 ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200329 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100330 ly_print_(pctx->out, ">");
331 lyxml_dump_text(pctx->out, value, 0);
332 ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200333 }
Radek Krejcia1911222019-07-22 17:24:50 +0200334 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100335 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200336 }
Michal Vasko569f2f42021-08-04 11:31:08 +0200337
338 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200339}
340
341/**
342 * @brief Print XML element representing lyd_node_inner.
343 *
344 * @param[in] ctx XML printer context.
345 * @param[in] node Data node to be printed.
346 * @return LY_ERR value.
347 */
348static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100349xml_print_inner(struct xmlpr_ctx *pctx, const struct lyd_node_inner *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200350{
351 LY_ERR ret;
352 struct lyd_node *child;
353
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100354 xml_print_node_open(pctx, &node->node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200355
Michal Vasko630d9892020-12-08 17:11:08 +0100356 LY_LIST_FOR(node->child, child) {
Michal Vasko8db584d2022-03-30 13:42:48 +0200357 if (lyd_node_should_print(child, pctx->options)) {
Michal Vasko630d9892020-12-08 17:11:08 +0100358 break;
359 }
360 }
361 if (!child) {
362 /* there are no children that will be printed */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100363 ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200364 return LY_SUCCESS;
365 }
366
367 /* children */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100368 ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200369
370 LEVEL_INC;
371 LY_LIST_FOR(node->child, child) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100372 ret = xml_print_node(pctx, child);
Radek Krejcie7b95092019-05-15 11:03:07 +0200373 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
374 }
375 LEVEL_DEC;
376
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100377 ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200378
379 return LY_SUCCESS;
380}
381
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200382static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100383xml_print_anydata(struct xmlpr_ctx *pctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200384{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200385 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200386 struct lyd_node *iter;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200387 uint32_t prev_opts, prev_lo;
Michal Vasko52927e22020-03-16 17:26:14 +0100388 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200389
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100390 xml_print_node_open(pctx, &node->node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200391
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200392 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200393 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200394no_content:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100395 ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200396 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200397 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200398 if (any->value_type == LYD_ANYDATA_LYB) {
399 /* turn logging off */
400 prev_lo = ly_log_options(0);
401
402 /* try to parse it into a data tree */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200403 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 +0200404 /* successfully parsed */
405 free(any->value.mem);
406 any->value.tree = iter;
407 any->value_type = LYD_ANYDATA_DATATREE;
408 }
Radek Krejci7931b192020-06-25 17:05:03 +0200409
410 /* turn loggin on again */
411 ly_log_options(prev_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200412 }
413
Radek Krejcie7b95092019-05-15 11:03:07 +0200414 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200415 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200416 /* close opening tag and print data */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100417 prev_opts = pctx->options;
418 pctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200419 LEVEL_INC;
420
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100421 ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200422 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100423 ret = xml_print_node(pctx, iter);
Michal Vasko52927e22020-03-16 17:26:14 +0100424 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200425 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200426
427 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100428 pctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200429 break;
430 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200431 /* escape XML-sensitive characters */
432 if (!any->value.str[0]) {
433 goto no_content;
434 }
435 /* close opening tag and print data */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100436 ly_print_(pctx->out, ">");
437 lyxml_dump_text(pctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200438 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200439 case LYD_ANYDATA_XML:
440 /* print without escaping special characters */
441 if (!any->value.str[0]) {
442 goto no_content;
443 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100444 ly_print_(pctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200445 break;
446 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200447 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200448 /* JSON and LYB format is not supported */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100449 LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200450 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200451 }
452
453 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200454 if (any->value_type == LYD_ANYDATA_DATATREE) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100455 ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200456 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100457 ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200458 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200459 }
460
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200461 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200462}
Radek Krejcie7b95092019-05-15 11:03:07 +0200463
Michal Vasko52927e22020-03-16 17:26:14 +0100464static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100465xml_print_opaq(struct xmlpr_ctx *pctx, const struct lyd_node_opaq *node)
Michal Vasko52927e22020-03-16 17:26:14 +0100466{
467 LY_ERR ret;
468 struct lyd_node *child;
Michal Vasko52927e22020-03-16 17:26:14 +0100469
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100470 LY_CHECK_RET(xml_print_opaq_open(pctx, node));
Michal Vasko52927e22020-03-16 17:26:14 +0100471
472 if (node->value[0]) {
473 /* print namespaces connected with the value's prefixes */
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100474 if (node->val_prefix_data) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100475 xml_print_ns_prefix_data(pctx, node->format, node->val_prefix_data, LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100476 }
477
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100478 ly_print_(pctx->out, ">");
479 lyxml_dump_text(pctx->out, node->value, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100480 }
481
482 if (node->child) {
483 /* children */
484 if (!node->value[0]) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100485 ly_print_(pctx->out, ">%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100486 }
487
488 LEVEL_INC;
489 LY_LIST_FOR(node->child, child) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100490 ret = xml_print_node(pctx, child);
Michal Vasko52927e22020-03-16 17:26:14 +0100491 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
492 }
493 LEVEL_DEC;
494
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100495 ly_print_(pctx->out, "%*s</%s>%s", INDENT, node->name.name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100496 } else if (node->value[0]) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100497 ly_print_(pctx->out, "</%s>%s", node->name.name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100498 } else {
499 /* no value or children */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100500 ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100501 }
502
503 return LY_SUCCESS;
504}
505
Radek Krejcie7b95092019-05-15 11:03:07 +0200506/**
507 * @brief Print XML element representing lyd_node.
508 *
509 * @param[in] ctx XML printer context.
510 * @param[in] node Data node to be printed.
511 * @return LY_ERR value.
512 */
513static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100514xml_print_node(struct xmlpr_ctx *pctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200515{
516 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100517 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200518
Michal Vasko8db584d2022-03-30 13:42:48 +0200519 if (!lyd_node_should_print(node, pctx->options)) {
Michal Vasko9b368d32020-02-14 13:53:31 +0100520 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100521 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200522 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200523
Michal Vasko52927e22020-03-16 17:26:14 +0100524 /* remember namespace definition count on this level */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100525 ns_count = pctx->ns.count;
Michal Vasko52927e22020-03-16 17:26:14 +0100526
527 if (!node->schema) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100528 ret = xml_print_opaq(pctx, (const struct lyd_node_opaq *)node);
Michal Vasko52927e22020-03-16 17:26:14 +0100529 } else {
530 switch (node->schema->nodetype) {
531 case LYS_CONTAINER:
532 case LYS_LIST:
533 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100534 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100535 case LYS_ACTION:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100536 ret = xml_print_inner(pctx, (const struct lyd_node_inner *)node);
Michal Vasko52927e22020-03-16 17:26:14 +0100537 break;
538 case LYS_LEAF:
539 case LYS_LEAFLIST:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100540 ret = xml_print_term(pctx, (const struct lyd_node_term *)node);
Michal Vasko52927e22020-03-16 17:26:14 +0100541 break;
542 case LYS_ANYXML:
543 case LYS_ANYDATA:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100544 ret = xml_print_anydata(pctx, (const struct lyd_node_any *)node);
Michal Vasko52927e22020-03-16 17:26:14 +0100545 break;
546 default:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100547 LOGINT(pctx->ctx);
Michal Vasko52927e22020-03-16 17:26:14 +0100548 ret = LY_EINT;
549 break;
550 }
551 }
552
553 /* remove all added namespaces */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100554 while (ns_count < pctx->ns.count) {
555 lydict_remove(pctx->ctx, pctx->prefix.objs[pctx->prefix.count - 1]);
556 ly_set_rm_index(&pctx->prefix, pctx->prefix.count - 1, NULL);
557 ly_set_rm_index(&pctx->ns, pctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200558 }
559
560 return ret;
561}
562
563LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200564xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200565{
566 const struct lyd_node *node;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100567 struct xmlpr_ctx pctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200568
569 if (!root) {
Michal Vasko69730152020-10-09 16:30:07 +0200570 if ((out->type == LY_OUT_MEMORY) || (out->type == LY_OUT_CALLBACK)) {
Michal Vasko5233e962020-08-14 14:26:20 +0200571 ly_print_(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200572 }
573 goto finish;
574 }
575
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100576 pctx.out = out;
577 pctx.level = 0;
578 pctx.options = options;
579 pctx.ctx = LYD_CTX(root);
Michal Vasko52927e22020-03-16 17:26:14 +0100580
Radek Krejcie7b95092019-05-15 11:03:07 +0200581 /* content */
582 LY_LIST_FOR(root, node) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100583 LY_CHECK_RET(xml_print_node(&pctx, node));
Radek Krejci7931b192020-06-25 17:05:03 +0200584 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200585 break;
586 }
587 }
588
589finish:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100590 assert(!pctx.prefix.count && !pctx.ns.count);
591 ly_set_erase(&pctx.prefix, NULL);
592 ly_set_erase(&pctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200593 ly_print_flush(out);
594 return LY_SUCCESS;
595}