blob: 2bc3facd31bd185258089cc1c434477ba0ebaca0 [file] [log] [blame]
Radek Krejcie7b95092019-05-15 11:03:07 +02001/**
2 * @file printer_xml.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @author Radek Krejci <rkrejci@cesnet.cz>
5 * @brief XML printer for libyang data structure
6 *
7 * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
Radek Krejci535ea9f2020-05-29 16:01:05 +020016#include <assert.h>
17#include <stdint.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020018#include <stdlib.h>
19#include <string.h>
20
Radek Krejci535ea9f2020-05-29 16:01:05 +020021#include "common.h"
22#include "context.h"
23#include "dict.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include "log.h"
Radek Krejci47fab892020-11-05 17:02:41 +010025#include "out.h"
26#include "out_internal.h"
Radek Krejci7931b192020-06-25 17:05:03 +020027#include "parser_data.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020028#include "plugins_types.h"
29#include "printer_data.h"
30#include "printer_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020031#include "set.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020032#include "tree.h"
Radek Krejci47fab892020-11-05 17:02:41 +010033#include "tree_data.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020034#include "tree_schema.h"
35#include "xml.h"
36
37/**
38 * @brief XML printer context.
39 */
40struct xmlpr_ctx {
Radek Krejci1deb5be2020-08-26 16:43:36 +020041 struct ly_out *out; /**< output specification */
42 uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
43 uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */
Michal Vasko52927e22020-03-16 17:26:14 +010044 const struct ly_ctx *ctx; /**< libyang context */
Radek Krejci1deb5be2020-08-26 16:43:36 +020045 struct ly_set prefix; /**< printed namespace prefixes */
46 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020047};
48
Michal Vasko52927e22020-03-16 17:26:14 +010049#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejci1798aae2020-07-14 13:26:06 +020050#define LYXML_PREFIX_DEFAULT 0x02 /**< The namespace is required to be a default (without prefix) */
Radek Krejcie7b95092019-05-15 11:03:07 +020051
52/**
Michal Vasko52927e22020-03-16 17:26:14 +010053 * @brief Print a namespace if not already printed.
54 *
55 * @param[in] ctx XML printer context.
56 * @param[in] ns Namespace to print, expected to be in dictionary.
57 * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
58 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
59 * @return Printed prefix of the namespace to use.
Radek Krejcie7b95092019-05-15 11:03:07 +020060 */
Michal Vasko52927e22020-03-16 17:26:14 +010061static const char *
Radek Krejci1deb5be2020-08-26 16:43:36 +020062xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, uint32_t prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020063{
Radek Krejciba03a5a2020-08-27 14:40:41 +020064 uint32_t i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010065
Radek Krejciba03a5a2020-08-27 14:40:41 +020066 for (i = ctx->ns.count; i > 0; --i) {
Michal Vasko52927e22020-03-16 17:26:14 +010067 if (!new_prefix) {
68 /* find default namespace */
Radek Krejciba03a5a2020-08-27 14:40:41 +020069 if (!ctx->prefix.objs[i - 1]) {
Michal Vasko6b5cb2a2020-11-11 19:11:21 +010070 if (!strcmp(ctx->ns.objs[i - 1], ns)) {
Radek Krejciba03a5a2020-08-27 14:40:41 +020071 /* matching default namespace */
72 return ctx->prefix.objs[i - 1];
Radek Krejcie7b95092019-05-15 11:03:07 +020073 }
Radek Krejciba03a5a2020-08-27 14:40:41 +020074 /* not matching default namespace */
Michal Vasko52927e22020-03-16 17:26:14 +010075 break;
76 }
77 } else {
78 /* find prefixed namespace */
Michal Vasko6b5cb2a2020-11-11 19:11:21 +010079 if (!strcmp(ctx->ns.objs[i - 1], ns)) {
Radek Krejciba03a5a2020-08-27 14:40:41 +020080 if (!ctx->prefix.objs[i - 1]) {
Michal Vasko52927e22020-03-16 17:26:14 +010081 /* default namespace is not interesting */
82 continue;
83 }
84
Radek Krejciba03a5a2020-08-27 14:40:41 +020085 if (!strcmp(ctx->prefix.objs[i - 1], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
Michal Vasko52927e22020-03-16 17:26:14 +010086 /* the same prefix or can be any */
Radek Krejciba03a5a2020-08-27 14:40:41 +020087 return ctx->prefix.objs[i - 1];
Michal Vasko52927e22020-03-16 17:26:14 +010088 }
89 }
Radek Krejcie7b95092019-05-15 11:03:07 +020090 }
Radek Krejcie7b95092019-05-15 11:03:07 +020091 }
Radek Krejci28681fa2019-09-06 13:08:45 +020092
Radek Krejciba03a5a2020-08-27 14:40:41 +020093 /* suitable namespace not found, must be printed */
94 ly_print_(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020095
Radek Krejciba03a5a2020-08-27 14:40:41 +020096 /* and added into namespaces */
97 if (new_prefix) {
Radek Krejci011e4aa2020-09-04 15:22:31 +020098 LY_CHECK_RET(lydict_insert(ctx->ctx, new_prefix, 0, &new_prefix), NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +020099 }
Radek Krejci3d92e442020-10-12 12:48:13 +0200100 LY_CHECK_RET(ly_set_add(&ctx->prefix, (void *)new_prefix, 1, NULL), NULL);
101 LY_CHECK_RET(ly_set_add(&ctx->ns, (void *)ns, 1, &i), NULL);
Michal Vasko52927e22020-03-16 17:26:14 +0100102
103 /* return it */
104 return ctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200105}
106
Michal Vasko22df3f02020-08-24 13:29:22 +0200107static const char *
Michal Vaskoad92b672020-11-12 13:11:31 +0100108xml_print_ns_opaq(struct xmlpr_ctx *ctx, LY_PREFIX_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) {
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100111 case LY_PREF_XML:
Michal Vaskoad92b672020-11-12 13:11:31 +0100112 return xml_print_ns(ctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
Radek Krejci1798aae2020-07-14 13:26:06 +0200113 break;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100114 case LY_PREF_JSON:
Michal Vaskoad92b672020-11-12 13:11:31 +0100115 if (name->module_name) {
116 const struct lys_module *mod = ly_ctx_get_module_latest(ctx->ctx, name->module_name);
Radek Krejci1798aae2020-07-14 13:26:06 +0200117 if (mod) {
Michal Vaskoad92b672020-11-12 13:11:31 +0100118 return xml_print_ns(ctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
Radek Krejci1798aae2020-07-14 13:26:06 +0200119 }
120 }
121 break;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100122 default:
Radek Krejci1798aae2020-07-14 13:26:06 +0200123 /* cannot be created */
124 LOGINT(ctx->ctx);
125 }
126
127 return NULL;
128}
129
Radek Krejciec9ad602021-01-04 10:46:30 +0100130/**
131 * @brief Print prefix data.
132 *
133 * @param[in] ctx XML printer context.
134 * @param[in] format Value prefix format.
135 * @param[in] prefix_data Format-specific data to print:
136 * LY_PREF_SCHEMA - const struct lysp_module * (module used for resolving prefixes from imports)
137 * LY_PREF_SCHEMA_RESOLVED - struct lyd_value_prefix * (sized array of pairs: prefix - module)
138 * LY_PREF_XML - const struct ly_set * (set with defined namespaces stored as ::lyxml_ns)
139 * LY_PREF_JSON - NULL
140 * @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
144xml_print_ns_prefix_data(struct xmlpr_ctx *ctx, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t prefix_opts)
145{
146 const struct ly_set *set;
147 const struct lyxml_ns *ns;
148 uint32_t i;
149
150 switch (format) {
151 case LY_PREF_XML:
152 set = prefix_data;
153 for (i = 0; i < set->count; ++i) {
154 ns = set->objs[i];
155 xml_print_ns(ctx, ns->uri, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : ns->prefix, prefix_opts);
156 }
157 break;
158 default:
159 /* cannot be created */
160 LOGINT(ctx->ctx);
161 }
162}
163
Radek Krejci28681fa2019-09-06 13:08:45 +0200164/**
Radek Krejcie7b95092019-05-15 11:03:07 +0200165 * TODO
166 */
Michal Vasko52927e22020-03-16 17:26:14 +0100167static void
Michal Vasko9f96a052020-03-10 09:41:45 +0100168xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200169{
Michal Vasko9f96a052020-03-10 09:41:45 +0100170 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100171 const struct lys_module *mod;
172 struct ly_set ns_list = {0};
Michal Vasko69730152020-10-09 16:30:07 +0200173
Radek Krejci28681fa2019-09-06 13:08:45 +0200174#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200175 const char **prefs, **nss;
176 const char *xml_expr = NULL, *mod_name;
177 uint32_t ns_count, i;
Radek Krejci857189e2020-09-01 13:26:36 +0200178 ly_bool rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200179 char *p;
180 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200181#endif
Radek Krejci857189e2020-09-01 13:26:36 +0200182 ly_bool dynamic;
Radek Krejcie7b95092019-05-15 11:03:07 +0200183
Radek Krejcie7b95092019-05-15 11:03:07 +0200184 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100185 if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci7931b192020-06-25 17:05:03 +0200186 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
Radek Krejci19611252020-10-04 13:54:53 +0200187 ((ctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100188 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
189 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
190 if (mod) {
Michal Vasko5233e962020-08-14 14:26:20 +0200191 ly_print_(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200192 }
193 }
194 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100195#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200196 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
Michal Vasko69730152020-10-09 16:30:07 +0200197 if (!strcmp(node->schema->name, "filter") &&
198 (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200199 rpc_filter = 1;
200 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200201#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100202 for (meta = node->meta; meta; meta = meta->next) {
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200203 const char *value = meta->value.realtype->plugin->print(&meta->value, LY_PREF_XML, &ns_list, &dynamic);
Radek Krejci28681fa2019-09-06 13:08:45 +0200204
Michal Vasko52927e22020-03-16 17:26:14 +0100205 /* print namespaces connected with the value's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200206 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100207 mod = (const struct lys_module *)ns_list.objs[u];
208 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200209 }
210 ly_set_erase(&ns_list, NULL);
211
212#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200213 if (rpc_filter) {
214 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100215 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200216 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100217 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200218 if (!xml_expr) {
219 /* error */
220 return EXIT_FAILURE;
221 }
222
223 for (i = 0; i < ns_count; ++i) {
Michal Vasko5233e962020-08-14 14:26:20 +0200224 ly_print_(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
Radek Krejcie7b95092019-05-15 11:03:07 +0200225 }
226 free(prefs);
227 free(nss);
228 }
Michal Vasko5233e962020-08-14 14:26:20 +0200229 ly_print_(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200230 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200231#endif
Radek Krejci0f969882020-08-21 16:56:47 +0200232 /* print the metadata with its namespace */
233 mod = meta->annotation->module;
234 ly_print_(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200235#if 0
Radek Krejci0f969882020-08-21 16:56:47 +0200236 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200237#endif
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]) {
241 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200242 }
Michal Vasko5233e962020-08-14 14:26:20 +0200243 ly_print_(ctx->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
Radek Krejcie7b95092019-05-15 11:03:07 +0200259xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
260{
Michal Vasko52927e22020-03-16 17:26:14 +0100261 /* print node name */
Michal Vasko5233e962020-08-14 14:26:20 +0200262 ly_print_(ctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100263
264 /* print default namespace */
265 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
266
267 /* print metadata */
268 xml_print_meta(ctx, node);
269}
270
271static LY_ERR
272xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
273{
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 Vaskoad92b672020-11-12 13:11:31 +0100281 pref = xml_print_ns_opaq(ctx, 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) {
286 xml_print_ns_prefix_data(ctx, 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 */
Radek IÅ¡a52c4ac62021-03-08 09:37:32 +0100290 ly_print_(ctx->out, " %s%s%s=\"", pref ? pref : "", pref ? ":" : "", attr->name.name);
291 lyxml_dump_text(ctx->out, attr->value, 1);
292 ly_print_(ctx->out, "\""); /* print attribute value terminator */
293
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
300xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
301{
302 /* print node name */
Michal Vaskoad92b672020-11-12 13:11:31 +0100303 ly_print_(ctx->out, "%*s<%s", INDENT, node->name.name);
Michal Vasko52927e22020-03-16 17:26:14 +0100304
305 /* print default namespace */
Michal Vaskoad92b672020-11-12 13:11:31 +0100306 xml_print_ns_opaq(ctx, node->format, &node->name, LYXML_PREFIX_DEFAULT);
Radek Krejcie7b95092019-05-15 11:03:07 +0200307
Michal Vasko52927e22020-03-16 17:26:14 +0100308 /* print attributes */
309 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200310
311 return LY_SUCCESS;
312}
313
314static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
315
316/**
317 * @brief Print XML element representing lyd_node_term.
318 *
319 * @param[in] ctx XML printer context.
320 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200321 */
Michal Vasko52927e22020-03-16 17:26:14 +0100322static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200323xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
324{
Radek Krejcia1911222019-07-22 17:24:50 +0200325 struct ly_set ns_list = {0};
Radek Krejci857189e2020-09-01 13:26:36 +0200326 ly_bool dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200327 const char *value;
328
Michal Vasko9e685082021-01-29 14:49:09 +0100329 xml_print_node_open(ctx, &node->node);
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200330 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 +0200331
Radek Krejcia1911222019-07-22 17:24:50 +0200332 /* print namespaces connected with the values's prefixes */
Radek Krejci1deb5be2020-08-26 16:43:36 +0200333 for (uint32_t u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100334 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Michal Vasko5233e962020-08-14 14:26:20 +0200335 ly_print_(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200336 }
Radek Krejcia1911222019-07-22 17:24:50 +0200337 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200338
Radek Krejcia1911222019-07-22 17:24:50 +0200339 if (!value || !value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200340 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200341 } else {
Michal Vasko5233e962020-08-14 14:26:20 +0200342 ly_print_(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200343 lyxml_dump_text(ctx->out, value, 0);
Michal Vasko5233e962020-08-14 14:26:20 +0200344 ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200345 }
Radek Krejcia1911222019-07-22 17:24:50 +0200346 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100347 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200348 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200349}
350
351/**
352 * @brief Print XML element representing lyd_node_inner.
353 *
354 * @param[in] ctx XML printer context.
355 * @param[in] node Data node to be printed.
356 * @return LY_ERR value.
357 */
358static LY_ERR
359xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
360{
361 LY_ERR ret;
362 struct lyd_node *child;
363
Michal Vasko9e685082021-01-29 14:49:09 +0100364 xml_print_node_open(ctx, &node->node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200365
Michal Vasko630d9892020-12-08 17:11:08 +0100366 LY_LIST_FOR(node->child, child) {
367 if (ly_should_print(child, ctx->options)) {
368 break;
369 }
370 }
371 if (!child) {
372 /* there are no children that will be printed */
Michal Vasko5233e962020-08-14 14:26:20 +0200373 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200374 return LY_SUCCESS;
375 }
376
377 /* children */
Michal Vasko5233e962020-08-14 14:26:20 +0200378 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200379
380 LEVEL_INC;
381 LY_LIST_FOR(node->child, child) {
382 ret = xml_print_node(ctx, child);
383 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
384 }
385 LEVEL_DEC;
386
Michal Vasko5233e962020-08-14 14:26:20 +0200387 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200388
389 return LY_SUCCESS;
390}
391
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200392static LY_ERR
393xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200394{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200395 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200396 struct lyd_node *iter;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200397 uint32_t prev_opts, prev_lo;
Michal Vasko52927e22020-03-16 17:26:14 +0100398 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200399
Michal Vasko9e685082021-01-29 14:49:09 +0100400 xml_print_node_open(ctx, &node->node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200401
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200402 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200403 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200404no_content:
Michal Vasko5233e962020-08-14 14:26:20 +0200405 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200406 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200407 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200408 if (any->value_type == LYD_ANYDATA_LYB) {
409 /* turn logging off */
410 prev_lo = ly_log_options(0);
411
412 /* try to parse it into a data tree */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200413 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 +0200414 /* successfully parsed */
415 free(any->value.mem);
416 any->value.tree = iter;
417 any->value_type = LYD_ANYDATA_DATATREE;
418 }
Radek Krejci7931b192020-06-25 17:05:03 +0200419
420 /* turn loggin on again */
421 ly_log_options(prev_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200422 }
423
Radek Krejcie7b95092019-05-15 11:03:07 +0200424 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200425 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200426 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100427 prev_opts = ctx->options;
Radek Krejci7931b192020-06-25 17:05:03 +0200428 ctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200429 LEVEL_INC;
430
Michal Vasko5233e962020-08-14 14:26:20 +0200431 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200432 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100433 ret = xml_print_node(ctx, iter);
434 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200435 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200436
437 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100438 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200439 break;
440 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200441 /* escape XML-sensitive characters */
442 if (!any->value.str[0]) {
443 goto no_content;
444 }
445 /* close opening tag and print data */
Michal Vasko5233e962020-08-14 14:26:20 +0200446 ly_print_(ctx->out, ">");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200447 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200448 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200449 case LYD_ANYDATA_XML:
450 /* print without escaping special characters */
451 if (!any->value.str[0]) {
452 goto no_content;
453 }
Michal Vasko5233e962020-08-14 14:26:20 +0200454 ly_print_(ctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200455 break;
456 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200457 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200458 /* JSON and LYB format is not supported */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200459 LOGWRN(LYD_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200460 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200461 }
462
463 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200464 if (any->value_type == LYD_ANYDATA_DATATREE) {
Michal Vasko5233e962020-08-14 14:26:20 +0200465 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200466 } else {
Michal Vasko5233e962020-08-14 14:26:20 +0200467 ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200468 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200469 }
470
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200471 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200472}
Radek Krejcie7b95092019-05-15 11:03:07 +0200473
Michal Vasko52927e22020-03-16 17:26:14 +0100474static LY_ERR
475xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
476{
477 LY_ERR ret;
478 struct lyd_node *child;
Michal Vasko52927e22020-03-16 17:26:14 +0100479
480 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
481
482 if (node->value[0]) {
483 /* print namespaces connected with the value's prefixes */
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100484 if (node->val_prefix_data) {
485 xml_print_ns_prefix_data(ctx, node->format, node->val_prefix_data, LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100486 }
487
Radek IÅ¡a52c4ac62021-03-08 09:37:32 +0100488 ly_print_(ctx->out, ">");
489 lyxml_dump_text(ctx->out, node->value, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100490 }
491
492 if (node->child) {
493 /* children */
494 if (!node->value[0]) {
Michal Vasko5233e962020-08-14 14:26:20 +0200495 ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100496 }
497
498 LEVEL_INC;
499 LY_LIST_FOR(node->child, child) {
500 ret = xml_print_node(ctx, child);
501 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
502 }
503 LEVEL_DEC;
504
Michal Vaskoad92b672020-11-12 13:11:31 +0100505 ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->name.name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100506 } else if (node->value[0]) {
Michal Vaskoad92b672020-11-12 13:11:31 +0100507 ly_print_(ctx->out, "</%s>%s", node->name.name, DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100508 } else {
509 /* no value or children */
Michal Vasko5233e962020-08-14 14:26:20 +0200510 ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100511 }
512
513 return LY_SUCCESS;
514}
515
Radek Krejcie7b95092019-05-15 11:03:07 +0200516/**
517 * @brief Print XML element representing lyd_node.
518 *
519 * @param[in] ctx XML printer context.
520 * @param[in] node Data node to be printed.
521 * @return LY_ERR value.
522 */
523static LY_ERR
524xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
525{
526 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100527 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200528
Michal Vasko9b368d32020-02-14 13:53:31 +0100529 if (!ly_should_print(node, ctx->options)) {
530 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100531 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200532 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200533
Michal Vasko52927e22020-03-16 17:26:14 +0100534 /* remember namespace definition count on this level */
535 ns_count = ctx->ns.count;
536
537 if (!node->schema) {
538 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
539 } else {
540 switch (node->schema->nodetype) {
541 case LYS_CONTAINER:
542 case LYS_LIST:
543 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100544 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100545 case LYS_ACTION:
546 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
547 break;
548 case LYS_LEAF:
549 case LYS_LEAFLIST:
550 xml_print_term(ctx, (const struct lyd_node_term *)node);
551 break;
552 case LYS_ANYXML:
553 case LYS_ANYDATA:
554 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
555 break;
556 default:
557 LOGINT(node->schema->module->ctx);
558 ret = LY_EINT;
559 break;
560 }
561 }
562
563 /* remove all added namespaces */
564 while (ns_count < ctx->ns.count) {
Michal Vaskoe180ed02021-02-05 16:31:20 +0100565 lydict_remove(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
Michal Vasko52927e22020-03-16 17:26:14 +0100566 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
567 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200568 }
569
570 return ret;
571}
572
573LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200574xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200575{
576 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100577 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200578
579 if (!root) {
Michal Vasko69730152020-10-09 16:30:07 +0200580 if ((out->type == LY_OUT_MEMORY) || (out->type == LY_OUT_CALLBACK)) {
Michal Vasko5233e962020-08-14 14:26:20 +0200581 ly_print_(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200582 }
583 goto finish;
584 }
585
Michal Vasko52927e22020-03-16 17:26:14 +0100586 ctx.out = out;
Radek Krejci17ede7a2020-11-13 10:38:30 +0100587 ctx.level = 0;
Michal Vasko52927e22020-03-16 17:26:14 +0100588 ctx.options = options;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200589 ctx.ctx = LYD_CTX(root);
Michal Vasko52927e22020-03-16 17:26:14 +0100590
Radek Krejcie7b95092019-05-15 11:03:07 +0200591 /* content */
592 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100593 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejci7931b192020-06-25 17:05:03 +0200594 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200595 break;
596 }
597 }
598
599finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100600 assert(!ctx.prefix.count && !ctx.ns.count);
601 ly_set_erase(&ctx.prefix, NULL);
602 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200603 ly_print_flush(out);
604 return LY_SUCCESS;
605}