blob: 8cb009de864aa9df690e26fed503ee1513f4e4bd [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
16#include "common.h"
17
18#include <stdlib.h>
19#include <string.h>
Michal Vasko52927e22020-03-16 17:26:14 +010020#include <assert.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020021
22#include "log.h"
23#include "plugins_types.h"
24#include "printer_data.h"
25#include "printer_internal.h"
26#include "tree.h"
27#include "tree_data.h"
28#include "tree_schema.h"
29#include "xml.h"
30
31/**
32 * @brief XML printer context.
33 */
34struct xmlpr_ctx {
35 struct lyout *out; /**< output specification */
36 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
37 int options; /**< [Data printer flags](@ref dataprinterflags) */
Michal Vasko52927e22020-03-16 17:26:14 +010038 const struct ly_ctx *ctx; /**< libyang context */
39 struct ly_set prefix; /**< printed namespace prefixes */
40 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020041};
42
43#define LEVEL ctx->level /**< current level */
44#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
45#define LEVEL_INC if (LEVEL) {LEVEL++;} /**< increase indentation level */
46#define LEVEL_DEC if (LEVEL) {LEVEL--;} /**< decrease indentation level */
47
Michal Vasko52927e22020-03-16 17:26:14 +010048#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejcie7b95092019-05-15 11:03:07 +020049
50/**
Michal Vasko52927e22020-03-16 17:26:14 +010051 * @brief Print a namespace if not already printed.
52 *
53 * @param[in] ctx XML printer context.
54 * @param[in] ns Namespace to print, expected to be in dictionary.
55 * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
56 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
57 * @return Printed prefix of the namespace to use.
Radek Krejcie7b95092019-05-15 11:03:07 +020058 */
Michal Vasko52927e22020-03-16 17:26:14 +010059static const char *
60xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, int prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020061{
Michal Vasko52927e22020-03-16 17:26:14 +010062 int i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010063
Michal Vasko52927e22020-03-16 17:26:14 +010064 for (i = ctx->ns.count - 1; i > -1; --i) {
65 if (!new_prefix) {
66 /* find default namespace */
67 if (!ctx->prefix.objs[i]) {
68 if (ctx->ns.objs[i] != ns) {
69 /* different default namespace */
70 i = -1;
Radek Krejcie7b95092019-05-15 11:03:07 +020071 }
Michal Vasko52927e22020-03-16 17:26:14 +010072 break;
73 }
74 } else {
75 /* find prefixed namespace */
76 if (ctx->ns.objs[i] == ns) {
77 if (!ctx->prefix.objs[i]) {
78 /* default namespace is not interesting */
79 continue;
80 }
81
82 if (!strcmp(ctx->prefix.objs[i], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
83 /* the same prefix or can be any */
84 break;
85 }
86 }
Radek Krejcie7b95092019-05-15 11:03:07 +020087 }
Radek Krejcie7b95092019-05-15 11:03:07 +020088 }
Radek Krejci28681fa2019-09-06 13:08:45 +020089
Michal Vasko52927e22020-03-16 17:26:14 +010090 if (i == -1) {
91 /* suitable namespace not found, must be printed */
92 ly_print(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020093
Michal Vasko52927e22020-03-16 17:26:14 +010094 /* and added into namespaces */
95 if (new_prefix) {
96 new_prefix = lydict_insert(ctx->ctx, new_prefix, 0);
97 }
98 ly_set_add(&ctx->prefix, (void *)new_prefix, LY_SET_OPT_USEASLIST);
99 i = ly_set_add(&ctx->ns, (void *)ns, LY_SET_OPT_USEASLIST);
Radek Krejcie7b95092019-05-15 11:03:07 +0200100 }
Michal Vasko52927e22020-03-16 17:26:14 +0100101
102 /* return it */
103 return ctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200104}
105
106/**
107 * @brief XML mapping of YANG modules to prefixes in values.
108 *
109 * Implementation of ly_clb_get_prefix
110 */
111static const char *
112xml_print_get_prefix(const struct lys_module *mod, void *private)
113{
114 struct ly_set *ns_list = (struct ly_set*)private;
115
116 ly_set_add(ns_list, (void*)mod, 0);
117 return mod->prefix;
Radek Krejcie7b95092019-05-15 11:03:07 +0200118}
119
120/**
121 * TODO
122 */
Michal Vasko52927e22020-03-16 17:26:14 +0100123static void
Michal Vasko9f96a052020-03-10 09:41:45 +0100124xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200125{
Michal Vasko9f96a052020-03-10 09:41:45 +0100126 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100127 const struct lys_module *mod;
128 struct ly_set ns_list = {0};
Radek Krejci28681fa2019-09-06 13:08:45 +0200129#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200130 const char **prefs, **nss;
131 const char *xml_expr = NULL, *mod_name;
132 uint32_t ns_count, i;
133 int rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200134 char *p;
135 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200136#endif
Radek Krejci28681fa2019-09-06 13:08:45 +0200137 int dynamic;
138 unsigned int u;
Radek Krejcie7b95092019-05-15 11:03:07 +0200139
Radek Krejcie7b95092019-05-15 11:03:07 +0200140 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100141 if (node->schema->nodetype & LYD_NODE_TERM) {
142 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG))) ||
143 ((ctx->options & LYDP_WD_ALL_TAG) && ly_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100144 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
145 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
146 if (mod) {
147 ly_print(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200148 }
149 }
150 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100151#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200152 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
153 if (!strcmp(node->schema->name, "filter")
154 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
155 rpc_filter = 1;
156 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200157#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100158 for (meta = node->meta; meta; meta = meta->next) {
159 const char *value = meta->value.realtype->plugin->print(&meta->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
Radek Krejci28681fa2019-09-06 13:08:45 +0200160
Michal Vasko52927e22020-03-16 17:26:14 +0100161 /* print namespaces connected with the value's prefixes */
Radek Krejci28681fa2019-09-06 13:08:45 +0200162 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100163 mod = (const struct lys_module *)ns_list.objs[u];
164 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200165 }
166 ly_set_erase(&ns_list, NULL);
167
168#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200169 if (rpc_filter) {
170 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100171 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200172 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100173 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200174 if (!xml_expr) {
175 /* error */
176 return EXIT_FAILURE;
177 }
178
179 for (i = 0; i < ns_count; ++i) {
180 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
181 }
182 free(prefs);
183 free(nss);
184 }
Michal Vasko9f96a052020-03-10 09:41:45 +0100185 ly_print(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200186 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200187#endif
Michal Vasko52927e22020-03-16 17:26:14 +0100188 /* print the metadata with its namespace */
189 mod = meta->annotation->module;
190 ly_print(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200191#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200192 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200193#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200194
Michal Vasko52927e22020-03-16 17:26:14 +0100195 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200196 if (value && value[0]) {
197 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200198 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200199 ly_print(ctx->out, "\"");
200 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100201 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200202 }
203 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200204}
205
206/**
207 * @brief Print generic XML element despite of the data node type.
208 *
209 * Prints the element name, attributes and necessary namespaces.
210 *
211 * @param[in] ctx XML printer context.
212 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200213 */
Michal Vasko52927e22020-03-16 17:26:14 +0100214static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200215xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
216{
Michal Vasko52927e22020-03-16 17:26:14 +0100217 /* print node name */
218 ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
219
220 /* print default namespace */
221 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
222
223 /* print metadata */
224 xml_print_meta(ctx, node);
225}
226
227static LY_ERR
228xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
229{
230 const struct ly_attr *attr;
231 const char *pref;
232 uint32_t u;
233
234 LY_LIST_FOR(node->attr, attr) {
235 pref = NULL;
236 if (attr->prefix.pref) {
237 /* print attribute namespace */
238 switch (attr->format) {
239 case LYD_XML:
240 pref = xml_print_ns(ctx, attr->prefix.ns, attr->prefix.pref, 0);
241 break;
242 case LYD_SCHEMA:
243 /* cannot be created */
244 LOGINT(node->ctx);
245 return LY_EINT;
246 }
247 }
248
249 /* print namespaces connected with the value's prefixes */
250 if (attr->val_prefs) {
251 LY_ARRAY_FOR(attr->val_prefs, u) {
252 xml_print_ns(ctx, attr->val_prefs[u].ns, attr->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
253 }
254 }
255
256 /* print the attribute with its prefix and value */
257 ly_print(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200258 }
259
Michal Vasko52927e22020-03-16 17:26:14 +0100260 return LY_SUCCESS;
261}
262
263static LY_ERR
264xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
265{
266 /* print node name */
267 ly_print(ctx->out, "%*s<%s", INDENT, node->name);
268
269 /* print default namespace */
270 switch (node->format) {
271 case LYD_XML:
272 xml_print_ns(ctx, node->prefix.ns, NULL, 0);
273 break;
274 case LYD_SCHEMA:
275 /* cannot be created */
276 LOGINT(node->ctx);
277 return LY_EINT;
Radek Krejcie7b95092019-05-15 11:03:07 +0200278 }
279
Michal Vasko52927e22020-03-16 17:26:14 +0100280 /* print attributes */
281 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200282
283 return LY_SUCCESS;
284}
285
286static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
287
288/**
289 * @brief Print XML element representing lyd_node_term.
290 *
291 * @param[in] ctx XML printer context.
292 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200293 */
Michal Vasko52927e22020-03-16 17:26:14 +0100294static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200295xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
296{
Radek Krejcia1911222019-07-22 17:24:50 +0200297 struct ly_set ns_list = {0};
298 unsigned int u;
299 int dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200300 const char *value;
301
Michal Vasko52927e22020-03-16 17:26:14 +0100302 xml_print_node_open(ctx, (struct lyd_node *)node);
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100303 value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(&node->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
Radek Krejcie7b95092019-05-15 11:03:07 +0200304
Radek Krejcia1911222019-07-22 17:24:50 +0200305 /* print namespaces connected with the values's prefixes */
306 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100307 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
308 ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200309 }
Radek Krejcia1911222019-07-22 17:24:50 +0200310 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200311
Radek Krejcia1911222019-07-22 17:24:50 +0200312 if (!value || !value[0]) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200313 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
314 } else {
315 ly_print(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200316 lyxml_dump_text(ctx->out, value, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200317 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
318 }
Radek Krejcia1911222019-07-22 17:24:50 +0200319 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100320 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200321 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200322}
323
324/**
325 * @brief Print XML element representing lyd_node_inner.
326 *
327 * @param[in] ctx XML printer context.
328 * @param[in] node Data node to be printed.
329 * @return LY_ERR value.
330 */
331static LY_ERR
332xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
333{
334 LY_ERR ret;
335 struct lyd_node *child;
336
Michal Vasko52927e22020-03-16 17:26:14 +0100337 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200338
339 if (!node->child) {
340 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
341 return LY_SUCCESS;
342 }
343
344 /* children */
345 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
346
347 LEVEL_INC;
348 LY_LIST_FOR(node->child, child) {
349 ret = xml_print_node(ctx, child);
350 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
351 }
352 LEVEL_DEC;
353
354 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
355
356 return LY_SUCCESS;
357}
358
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200359static LY_ERR
360xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200361{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200362 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200363 struct lyd_node *iter;
Michal Vasko52927e22020-03-16 17:26:14 +0100364 int prev_opts;
365 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200366
Michal Vasko52927e22020-03-16 17:26:14 +0100367 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200368
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200369 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200370 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200371no_content:
372 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
373 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200374 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200375 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200376 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200377 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100378 prev_opts = ctx->options;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200379 ctx->options &= ~(LYDP_WITHSIBLINGS | LYDP_NETCONF);
380 LEVEL_INC;
381
382 ly_print(ctx->out, ">%s", LEVEL ? "\n" : "");
383 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100384 ret = xml_print_node(ctx, iter);
385 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200386 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200387
388 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100389 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200390 break;
391 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200392 /* escape XML-sensitive characters */
393 if (!any->value.str[0]) {
394 goto no_content;
395 }
396 /* close opening tag and print data */
397 ly_print(ctx->out, ">");
398 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200399 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200400 case LYD_ANYDATA_XML:
401 /* print without escaping special characters */
402 if (!any->value.str[0]) {
403 goto no_content;
404 }
405 ly_print(ctx->out, ">%s", any->value.str);
406 break;
407 case LYD_ANYDATA_JSON:
408#if 0 /* TODO LYB format */
409 case LYD_ANYDATA_LYB:
410#endif
411 /* JSON and LYB format is not supported */
412 LOGWRN(node->schema->module->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
413 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200414 }
415
416 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200417 if (any->value_type == LYD_ANYDATA_DATATREE) {
418 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
419 } else {
420 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
421 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200422 }
423
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200424 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200425}
Radek Krejcie7b95092019-05-15 11:03:07 +0200426
Michal Vasko52927e22020-03-16 17:26:14 +0100427static LY_ERR
428xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
429{
430 LY_ERR ret;
431 struct lyd_node *child;
432 uint32_t u;
433
434 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
435
436 if (node->value[0]) {
437 /* print namespaces connected with the value's prefixes */
438 if (node->val_prefs) {
439 LY_ARRAY_FOR(node->val_prefs, u) {
440 xml_print_ns(ctx, node->val_prefs[u].ns, node->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
441 }
442 }
443
444 ly_print(ctx->out, ">%s", node->value);
445 }
446
447 if (node->child) {
448 /* children */
449 if (!node->value[0]) {
450 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
451 }
452
453 LEVEL_INC;
454 LY_LIST_FOR(node->child, child) {
455 ret = xml_print_node(ctx, child);
456 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
457 }
458 LEVEL_DEC;
459
460 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->name, LEVEL ? "\n" : "");
461 } else if (node->value[0]) {
462 ly_print(ctx->out, "</%s>%s", node->name, LEVEL ? "\n" : "");
463 } else {
464 /* no value or children */
465 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
466 }
467
468 return LY_SUCCESS;
469}
470
Radek Krejcie7b95092019-05-15 11:03:07 +0200471/**
472 * @brief Print XML element representing lyd_node.
473 *
474 * @param[in] ctx XML printer context.
475 * @param[in] node Data node to be printed.
476 * @return LY_ERR value.
477 */
478static LY_ERR
479xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
480{
481 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100482 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200483
Michal Vasko9b368d32020-02-14 13:53:31 +0100484 if (!ly_should_print(node, ctx->options)) {
485 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100486 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200487 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200488
Michal Vasko52927e22020-03-16 17:26:14 +0100489 /* remember namespace definition count on this level */
490 ns_count = ctx->ns.count;
491
492 if (!node->schema) {
493 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
494 } else {
495 switch (node->schema->nodetype) {
496 case LYS_CONTAINER:
497 case LYS_LIST:
498 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100499 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100500 case LYS_ACTION:
501 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
502 break;
503 case LYS_LEAF:
504 case LYS_LEAFLIST:
505 xml_print_term(ctx, (const struct lyd_node_term *)node);
506 break;
507 case LYS_ANYXML:
508 case LYS_ANYDATA:
509 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
510 break;
511 default:
512 LOGINT(node->schema->module->ctx);
513 ret = LY_EINT;
514 break;
515 }
516 }
517
518 /* remove all added namespaces */
519 while (ns_count < ctx->ns.count) {
520 FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
521 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
522 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200523 }
524
525 return ret;
526}
527
528LY_ERR
529xml_print_data(struct lyout *out, const struct lyd_node *root, int options)
530{
531 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100532 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200533
534 if (!root) {
535 if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
536 ly_print(out, "");
537 }
538 goto finish;
539 }
540
Michal Vasko52927e22020-03-16 17:26:14 +0100541 ctx.out = out;
542 ctx.level = (options & LYDP_FORMAT ? 1 : 0);
543 ctx.options = options;
544 ctx.ctx = LYD_NODE_CTX(root);
545
Radek Krejcie7b95092019-05-15 11:03:07 +0200546 /* content */
547 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100548 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200549 if (!(options & LYDP_WITHSIBLINGS)) {
550 break;
551 }
552 }
553
554finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100555 assert(!ctx.prefix.count && !ctx.ns.count);
556 ly_set_erase(&ctx.prefix, NULL);
557 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200558 ly_print_flush(out);
559 return LY_SUCCESS;
560}
561