blob: 8d2dd011f996020c6e2b2d4f6770d274c4940ade [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>
20
21#include "log.h"
22#include "plugins_types.h"
23#include "printer_data.h"
24#include "printer_internal.h"
25#include "tree.h"
26#include "tree_data.h"
27#include "tree_schema.h"
28#include "xml.h"
29
30/**
31 * @brief XML printer context.
32 */
33struct xmlpr_ctx {
34 struct lyout *out; /**< output specification */
35 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
36 int options; /**< [Data printer flags](@ref dataprinterflags) */
37 int toplevel; /**< top-level flag */
38};
39
40#define LEVEL ctx->level /**< current level */
41#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
42#define LEVEL_INC if (LEVEL) {LEVEL++;} /**< increase indentation level */
43#define LEVEL_DEC if (LEVEL) {LEVEL--;} /**< decrease indentation level */
44
45/**
46 * TODO
47 */
48struct mlist {
49 struct mlist *next;
50 struct lys_module *module;
51} *mlist = NULL, *mlist_new;
52
53#if 0
54static LY_ERR
55modlist_add(struct mlist **mlist, const struct lys_module *mod)
56{
57 struct mlist *iter;
58
59 for (iter = *mlist; iter; iter = iter->next) {
60 if (mod == iter->module) {
61 break;
62 }
63 }
64
65 if (!iter) {
66 iter = malloc(sizeof *iter);
67 LY_CHECK_ERR_RET(!iter, LOGMEM(mod->ctx), LY_EMEM);
68 iter->next = *mlist;
69 iter->module = (struct lys_module *)mod;
70 *mlist = iter;
71 }
72
73 return LY_SUCCESS;
74}
75#endif
76
77/**
78 * TODO
79 */
80static void
81xml_print_ns(struct xmlpr_ctx *ctx, const struct lyd_node *node)
82{
83 struct lyd_node *next, *cur, *child;
84 struct lyd_attr *attr;
Radek Krejcie7b95092019-05-15 11:03:07 +020085
86#if 0
Radek Krejci585f1922019-05-17 10:34:15 +020087 struct mlist *mlist = NULL, *miter;
88
Radek Krejcie7b95092019-05-15 11:03:07 +020089 const struct lys_module *wdmod = NULL;
90
91 /* add node attribute modules */
92 for (attr = node->attr; attr; attr = attr->next) {
93 if (!strcmp(node->schema->name, "filter") &&
94 (!strcmp(node->schema->module->name, "ietf-netconf") ||
95 !strcmp(node->schema->module->name, "notifications"))) {
96 /* exception for NETCONF's filter attributes */
97 continue;
98 } else {
99 r = modlist_add(&mlist, lys_main_module(attr->annotation->module));
100 }
101 if (r) {
102 goto print;
103 }
104 }
105#endif
106
107 /* add node children nodes and attribute modules */
108 switch (node->schema->nodetype) {
109 case LYS_LEAFLIST:
110 case LYS_LEAF:
111 /* TODO ietf-netconf-with-defaults namespace */
112#if 0
113 if (node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) {
114 /* get with-defaults module and print its namespace */
115 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
116 if (wdmod && modlist_add(&mlist, wdmod)) {
117 goto print;
118 }
119 }
120#endif
121 break;
122 case LYS_CONTAINER:
123 case LYS_LIST:
124#if 0
125 case LYS_RPC:
126 case LYS_ACTION:
127 case LYS_NOTIF:
128 if (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG)) {
129 /* get with-defaults module and print its namespace */
130 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
131 if (wdmod && modlist_add(&mlist, wdmod)) {
132 goto print;
133 }
134 }
135#endif
136 LY_LIST_FOR(((struct lyd_node_inner*)node)->child, child) {
137 LYD_TREE_DFS_BEGIN(child, next, cur) {
138 for (attr = cur->attr; attr; attr = attr->next) {
139 if (!strcmp(cur->schema->name, "filter") &&
140 (!strcmp(cur->schema->module->name, "ietf-netconf") ||
141 !strcmp(cur->schema->module->name, "notifications"))) {
142 /* exception for NETCONF's filter attributes */
143 continue;
144 } else {
145 /* TODO annotations r = modlist_add(&mlist, lys_main_module(attr->annotation->module)); */
146 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200147 }
148 LYD_TREE_DFS_END(child, next, cur)}
149 }
150 break;
151 default:
152 break;
153 }
Radek Krejci585f1922019-05-17 10:34:15 +0200154#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200155print:
156 /* print used namespaces */
157 while (mlist) {
158 miter = mlist;
159 mlist = mlist->next;
160
161 ly_print(ctx->out, " xmlns:%s=\"%s\"", miter->module->prefix, miter->module->ns);
162 free(miter);
163 }
Radek Krejci585f1922019-05-17 10:34:15 +0200164#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200165}
166
167/**
168 * TODO
169 */
170static LY_ERR
171xml_print_attrs(struct xmlpr_ctx *ctx, const struct lyd_node *node)
172{
173 (void) ctx;
174 (void) node;
175
176#if 0
177 struct lyd_attr *attr;
178 const char **prefs, **nss;
179 const char *xml_expr = NULL, *mod_name;
180 uint32_t ns_count, i;
181 int rpc_filter = 0;
182 const struct lys_module *wdmod = NULL;
183 char *p;
184 size_t len;
185
186 LY_PRINT_SET;
187
188 /* with-defaults */
189 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
190 if ((node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) ||
191 (!node->dflt && (options & LYP_WD_ALL_TAG) && lyd_wd_default((struct lyd_node_leaf_list *)node))) {
192 /* we have implicit OR explicit default node */
193 /* get with-defaults module */
194 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
195 if (wdmod) {
196 /* print attribute only if context include with-defaults schema */
197 ly_print(out, " %s:default=\"true\"", wdmod->prefix);
198 }
199 }
200 }
201 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
202 if (!strcmp(node->schema->name, "filter")
203 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
204 rpc_filter = 1;
205 }
206
207 for (attr = node->attr; attr; attr = attr->next) {
208 if (rpc_filter) {
209 /* exception for NETCONF's filter's attributes */
210 if (!strcmp(attr->name, "select")) {
211 /* xpath content, we have to convert the JSON format into XML first */
212 xml_expr = transform_json2xml(node->schema->module, attr->value_str, 0, &prefs, &nss, &ns_count);
213 if (!xml_expr) {
214 /* error */
215 return EXIT_FAILURE;
216 }
217
218 for (i = 0; i < ns_count; ++i) {
219 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
220 }
221 free(prefs);
222 free(nss);
223 }
224 ly_print(out, " %s=\"", attr->name);
225 } else {
226 ly_print(out, " %s:%s=\"", attr->annotation->module->prefix, attr->name);
227 }
228
229 switch (attr->value_type) {
230 case LY_TYPE_BINARY:
231 case LY_TYPE_STRING:
232 case LY_TYPE_BITS:
233 case LY_TYPE_ENUM:
234 case LY_TYPE_BOOL:
235 case LY_TYPE_DEC64:
236 case LY_TYPE_INT8:
237 case LY_TYPE_INT16:
238 case LY_TYPE_INT32:
239 case LY_TYPE_INT64:
240 case LY_TYPE_UINT8:
241 case LY_TYPE_UINT16:
242 case LY_TYPE_UINT32:
243 case LY_TYPE_UINT64:
244 if (attr->value_str) {
245 /* xml_expr can contain transformed xpath */
246 lyxml_dump_text(out, xml_expr ? xml_expr : attr->value_str, LYXML_DATA_ATTR);
247 }
248 break;
249
250 case LY_TYPE_IDENT:
251 if (!attr->value_str) {
252 break;
253 }
254 p = strchr(attr->value_str, ':');
255 assert(p);
256 len = p - attr->value_str;
257 mod_name = attr->annotation->module->name;
258 if (!strncmp(attr->value_str, mod_name, len) && !mod_name[len]) {
259 lyxml_dump_text(out, ++p, LYXML_DATA_ATTR);
260 } else {
261 /* avoid code duplication - use instance-identifier printer which gets necessary namespaces to print */
262 goto printinst;
263 }
264 break;
265 case LY_TYPE_INST:
266printinst:
267 xml_expr = transform_json2xml(node->schema->module, ((struct lyd_node_leaf_list *)node)->value_str, 1,
268 &prefs, &nss, &ns_count);
269 if (!xml_expr) {
270 /* error */
271 return EXIT_FAILURE;
272 }
273
274 for (i = 0; i < ns_count; ++i) {
275 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
276 }
277 free(prefs);
278 free(nss);
279
280 lyxml_dump_text(out, xml_expr, LYXML_DATA_ATTR);
281 lydict_remove(node->schema->module->ctx, xml_expr);
282 break;
283
284 /* LY_TYPE_LEAFREF not allowed */
285 case LY_TYPE_EMPTY:
286 break;
287
288 default:
289 /* error */
290 LOGINT(node->schema->module->ctx);
291 return EXIT_FAILURE;
292 }
293
294 ly_print(out, "\"");
295
296 if (xml_expr) {
297 lydict_remove(node->schema->module->ctx, xml_expr);
298 }
299 }
300#endif
301
302 return LY_SUCCESS;
303}
304
305/**
306 * @brief Print generic XML element despite of the data node type.
307 *
308 * Prints the element name, attributes and necessary namespaces.
309 *
310 * @param[in] ctx XML printer context.
311 * @param[in] node Data node to be printed.
312 * @return LY_ERR value.
313 */
314static LY_ERR
315xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
316{
317 if (ctx->toplevel || !node->parent || node->schema->module != node->parent->schema->module) {
318 /* print "namespace" */
319 ly_print(ctx->out, "%*s<%s xmlns=\"%s\"", INDENT, node->schema->name, node->schema->module->ns);
320 } else {
321 ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
322 }
323
324 if (ctx->toplevel) {
325 xml_print_ns(ctx, node);
326 ctx->toplevel = 0;
327 }
328
329 LY_CHECK_RET(xml_print_attrs(ctx, node));
330
331 return LY_SUCCESS;
332}
333
334static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
335
336/**
Radek Krejcia1911222019-07-22 17:24:50 +0200337 * @brief XML mapping of YANG modules to prefixes in values.
338 *
339 * Implementation of ly_clb_get_prefix
340 */
341static const char *
342xml_print_get_prefix(const struct lys_module *mod, void *private)
343{
344 struct ly_set *ns_list = (struct ly_set*)private;
345
346 ly_set_add(ns_list, (void*)mod, 0);
347 return mod->prefix;
348}
349
350/**
Radek Krejcie7b95092019-05-15 11:03:07 +0200351 * @brief Print XML element representing lyd_node_term.
352 *
353 * @param[in] ctx XML printer context.
354 * @param[in] node Data node to be printed.
355 * @return LY_ERR value.
356 */
357static LY_ERR
358xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
359{
360 LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
Radek Krejcia1911222019-07-22 17:24:50 +0200361 struct ly_set ns_list = {0};
362 unsigned int u;
363 int dynamic;
364 const char *value = node->value.realtype->plugin->print(&node->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
Radek Krejcie7b95092019-05-15 11:03:07 +0200365
Radek Krejcia1911222019-07-22 17:24:50 +0200366 /* print namespaces connected with the values's prefixes */
367 for (u = 0; u < ns_list.count; ++u) {
368 const struct lys_module *mod = (const struct lys_module*)ns_list.objs[u];
369 ly_print(ctx->out, "%sxmlns:%s=\"%s\"", u ? " " : "", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200370 }
Radek Krejcia1911222019-07-22 17:24:50 +0200371 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200372
Radek Krejcia1911222019-07-22 17:24:50 +0200373 if (!value || !value[0]) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200374 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
375 } else {
376 ly_print(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200377 lyxml_dump_text(ctx->out, value, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200378 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
379 }
Radek Krejcia1911222019-07-22 17:24:50 +0200380 if (dynamic) {
381 free((void*)value);
382 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200383
384 return LY_SUCCESS;
385}
386
387/**
388 * @brief Print XML element representing lyd_node_inner.
389 *
390 * @param[in] ctx XML printer context.
391 * @param[in] node Data node to be printed.
392 * @return LY_ERR value.
393 */
394static LY_ERR
395xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
396{
397 LY_ERR ret;
398 struct lyd_node *child;
399
400 LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
401
402 if (!node->child) {
403 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
404 return LY_SUCCESS;
405 }
406
407 /* children */
408 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
409
410 LEVEL_INC;
411 LY_LIST_FOR(node->child, child) {
412 ret = xml_print_node(ctx, child);
413 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
414 }
415 LEVEL_DEC;
416
417 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
418
419 return LY_SUCCESS;
420}
421
422#if 0
423static int
424xml_print_anydata(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
425{
426 char *buf;
427 struct lyd_node_anydata *any = (struct lyd_node_anydata *)node;
428 struct lyd_node *iter;
429 const char *ns;
430
431 LY_PRINT_SET;
432
433 if (toplevel || !node->parent || nscmp(node, node->parent)) {
434 /* print "namespace" */
435 ns = lyd_node_module(node)->ns;
436 ly_print(out, "%*s<%s xmlns=\"%s\"", INDENT, node->schema->name, ns);
437 } else {
438 ly_print(out, "%*s<%s", INDENT, node->schema->name);
439 }
440
441 if (toplevel) {
442 xml_print_ns(out, node, options);
443 }
444 if (xml_print_attrs(out, node, options)) {
445 return EXIT_FAILURE;
446 }
447 if (!(void*)any->value.tree || (any->value_type == LYD_ANYDATA_CONSTSTRING && !any->value.str[0])) {
448 /* no content */
449 ly_print(out, "/>%s", level ? "\n" : "");
450 } else {
451 if (any->value_type == LYD_ANYDATA_DATATREE) {
452 /* print namespaces in the anydata data tree */
453 LY_TREE_FOR(any->value.tree, iter) {
454 xml_print_ns(out, iter, options);
455 }
456 }
457 /* close opening tag ... */
458 ly_print(out, ">");
459 /* ... and print anydata content */
460 switch (any->value_type) {
461 case LYD_ANYDATA_CONSTSTRING:
462 lyxml_dump_text(out, any->value.str, LYXML_DATA_ELEM);
463 break;
464 case LYD_ANYDATA_DATATREE:
465 if (any->value.tree) {
466 if (level) {
467 ly_print(out, "\n");
468 }
469 LY_TREE_FOR(any->value.tree, iter) {
470 if (xml_print_node(out, level ? level + 1 : 0, iter, 0, (options & ~(LYP_WITHSIBLINGS | LYP_NETCONF)))) {
471 return EXIT_FAILURE;
472 }
473 }
474 }
475 break;
476 case LYD_ANYDATA_XML:
477 lyxml_print_mem(&buf, any->value.xml, (level ? LYXML_PRINT_FORMAT | LYXML_PRINT_NO_LAST_NEWLINE : 0)
478 | LYXML_PRINT_SIBLINGS);
479 ly_print(out, "%s%s", level ? "\n" : "", buf);
480 free(buf);
481 break;
482 case LYD_ANYDATA_SXML:
483 /* print without escaping special characters */
484 ly_print(out, "%s", any->value.str);
485 break;
486 case LYD_ANYDATA_JSON:
487 case LYD_ANYDATA_LYB:
488 /* JSON and LYB format is not supported */
489 LOGWRN(node->schema->module->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
490 break;
491 case LYD_ANYDATA_STRING:
492 case LYD_ANYDATA_SXMLD:
493 case LYD_ANYDATA_JSOND:
494 case LYD_ANYDATA_LYBD:
495 /* dynamic strings are used only as input parameters */
496 assert(0);
497 break;
498 }
499
500 /* closing tag */
501 ly_print(out, "</%s>%s", node->schema->name, level ? "\n" : "");
502 }
503
504 LY_PRINT_RET(node->schema->module->ctx);
505}
506#endif
507
508/**
509 * @brief Print XML element representing lyd_node.
510 *
511 * @param[in] ctx XML printer context.
512 * @param[in] node Data node to be printed.
513 * @return LY_ERR value.
514 */
515static LY_ERR
516xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
517{
518 LY_ERR ret = LY_SUCCESS;
519
520#if 0
521 if (!lyd_wd_toprint(node, ctx->options)) {
522 /* wd says do not print */
523 return EXIT_SUCCESS;
524 }
525#endif
526
527 switch (node->schema->nodetype) {
528#if 0
529 case LYS_NOTIF:
530 case LYS_ACTION:
531#endif
532 case LYS_CONTAINER:
533 case LYS_LIST:
534 ret = xml_print_inner(ctx, (const struct lyd_node_inner*)node);
535 break;
536 case LYS_LEAF:
537 case LYS_LEAFLIST:
538 ret = xml_print_term(ctx, (const struct lyd_node_term*)node);
539 break;
540#if 0
541 case LYS_ANYXML:
542 case LYS_ANYDATA:
543 ret = xml_print_anydata(ctx, node);
544 break;
545#endif
546 default:
547 LOGINT(node->schema->module->ctx);
548 ret = LY_EINT;
549 break;
550 }
551
552 return ret;
553}
554
555LY_ERR
556xml_print_data(struct lyout *out, const struct lyd_node *root, int options)
557{
558 const struct lyd_node *node;
559 struct xmlpr_ctx ctx_ = {.out = out, .level = (options & LYDP_FORMAT ? 1 : 0), .options = options, .toplevel = 1}, *ctx = &ctx_;
560
561 if (!root) {
562 if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
563 ly_print(out, "");
564 }
565 goto finish;
566 }
567
568 /* content */
569 LY_LIST_FOR(root, node) {
570 if (xml_print_node(ctx, node)) {
571 return EXIT_FAILURE;
572 }
573 if (!(options & LYDP_WITHSIBLINGS)) {
574 break;
575 }
576 }
577
578finish:
579 ly_print_flush(out);
580 return LY_SUCCESS;
581}
582