blob: 990d520155aca6c733a39d1713024a924188c6ef [file] [log] [blame]
/**
* @file printer_xml.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief XML printer for libyang data structure
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
#include "common.h"
#include "printer.h"
#include "xml_internal.h"
#include "tree_data.h"
#include "tree_schema.h"
#include "resolve.h"
#include "tree_internal.h"
#define INDENT ""
#define LEVEL (level ? level*2-2 : 0)
void xml_print_node(struct lyout *out, int level, const struct lyd_node *node, int toplevel);
static void
xml_print_ns(struct lyout *out, const struct lyd_node *node)
{
struct lyd_node *next, *cur, *node2;
struct lyd_attr *attr;
struct mlist {
struct mlist *next;
struct lys_module *module;
} *mlist = NULL, *mlist_new;
assert(out);
assert(node);
for (attr = node->attr; attr; attr = attr->next) {
for (mlist_new = mlist; mlist_new; mlist_new = mlist_new->next) {
if (attr->module == mlist_new->module) {
break;
}
}
if (!mlist_new) {
mlist_new = malloc(sizeof *mlist_new);
if (!mlist_new) {
LOGMEM;
goto print;
}
mlist_new->next = mlist;
mlist_new->module = attr->module;
mlist = mlist_new;
}
}
if (!(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
node2 = (struct lyd_node *)node;
LY_TREE_FOR(node->child, node2) {
LY_TREE_DFS_BEGIN(node2, next, cur) {
for (attr = cur->attr; attr; attr = attr->next) {
for (mlist_new = mlist; mlist_new; mlist_new = mlist_new->next) {
if (attr->module == mlist_new->module) {
break;
}
}
if (!mlist_new) {
mlist_new = malloc(sizeof *mlist_new);
if (!mlist_new) {
LOGMEM
;
goto print;
}
mlist_new->next = mlist;
mlist_new->module = attr->module;
mlist = mlist_new;
}
}
LY_TREE_DFS_END(node2, next, cur)}
}
}
print:
/* print used namespaces */
while (mlist) {
mlist_new = mlist;
mlist = mlist->next;
ly_print(out, " xmlns:%s=\"%s\"", mlist_new->module->prefix, mlist_new->module->ns);
free(mlist_new);
}
}
static void
xml_print_attrs(struct lyout *out, const struct lyd_node *node)
{
struct lyd_attr *attr;
const char **prefs, **nss;
const char *xml_expr;
uint32_t ns_count, i;
int rpc_filter = 0;
/* technically, check for the extension get-filter-element-attributes from ietf-netconf */
if (!strcmp(node->schema->name, "filter")
&& (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
rpc_filter = 1;
}
for (attr = node->attr; attr; attr = attr->next) {
if (rpc_filter && !strcmp(attr->name, "type")) {
ly_print(out, " %s=\"", attr->name);
} else if (rpc_filter && !strcmp(attr->name, "select")) {
xml_expr = transform_json2xml(node->schema->module, attr->value, &prefs, &nss, &ns_count);
if (!xml_expr) {
/* error */
ly_print(out, "\"(!error!)\"");
return;
}
for (i = 0; i < ns_count; ++i) {
ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
}
free(prefs);
free(nss);
ly_print(out, " %s=\"", attr->name);
lyxml_dump_text(out, xml_expr);
ly_print(out, "\"");
lydict_remove(node->schema->module->ctx, xml_expr);
continue;
} else {
ly_print(out, " %s:%s=\"", attr->module->prefix, attr->name);
}
lyxml_dump_text(out, attr->value);
ly_print(out, "\"");
}
}
static void
xml_print_leaf(struct lyout *out, int level, const struct lyd_node *node, int toplevel)
{
const struct lyd_node_leaf_list *leaf = (struct lyd_node_leaf_list *)node;
const char *ns;
const char **prefs, **nss;
const char *xml_expr;
uint32_t ns_count, i;
if (!node->parent || nscmp(node, node->parent)) {
/* print "namespace" */
if (node->schema->module->type) {
/* submodule, get module */
ns = ((struct lys_submodule *)node->schema->module)->belongsto->ns;
} else {
ns = node->schema->module->ns;
}
ly_print(out, "%*s<%s xmlns=\"%s\"", LEVEL, INDENT, node->schema->name, ns);
} else {
ly_print(out, "%*s<%s", LEVEL, INDENT, node->schema->name);
}
if (toplevel) {
xml_print_ns(out, node);
}
xml_print_attrs(out, node);
switch (leaf->value_type & LY_DATA_TYPE_MASK) {
case LY_TYPE_BINARY:
case LY_TYPE_STRING:
case LY_TYPE_BITS:
case LY_TYPE_ENUM:
case LY_TYPE_BOOL:
case LY_TYPE_DEC64:
case LY_TYPE_INT8:
case LY_TYPE_INT16:
case LY_TYPE_INT32:
case LY_TYPE_INT64:
case LY_TYPE_UINT8:
case LY_TYPE_UINT16:
case LY_TYPE_UINT32:
case LY_TYPE_UINT64:
if (!leaf->value_str) {
ly_print(out, "/>");
} else {
ly_print(out, ">");
lyxml_dump_text(out, leaf->value_str);
ly_print(out, "</%s>", node->schema->name);
}
break;
case LY_TYPE_IDENT:
case LY_TYPE_INST:
xml_expr = transform_json2xml(node->schema->module, ((struct lyd_node_leaf_list *)node)->value_str,
&prefs, &nss, &ns_count);
if (!xml_expr) {
/* error */
ly_print(out, "\"(!error!)\"");
return;
}
for (i = 0; i < ns_count; ++i) {
ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
}
free(prefs);
free(nss);
if (xml_expr[0]) {
ly_print(out, ">");
lyxml_dump_text(out, xml_expr);
ly_print(out, "</%s>", node->schema->name);
} else {
ly_print(out, "/>");
}
lydict_remove(node->schema->module->ctx, xml_expr);
break;
case LY_TYPE_LEAFREF:
ly_print(out, ">");
lyxml_dump_text(out, ((struct lyd_node_leaf_list *)(leaf->value.leafref))->value_str);
ly_print(out, "</%s>", node->schema->name);
break;
case LY_TYPE_EMPTY:
ly_print(out, "/>");
break;
default:
/* error */
ly_print(out, "\"(!error!)\"");
}
if (level) {
ly_print(out, "\n");
}
}
static void
xml_print_container(struct lyout *out, int level, const struct lyd_node *node, int toplevel)
{
struct lyd_node *child;
const char *ns;
if (!node->parent || nscmp(node, node->parent)) {
/* print "namespace" */
if (node->schema->module->type) {
/* submodule, get module */
ns = ((struct lys_submodule *)node->schema->module)->belongsto->ns;
} else {
ns = node->schema->module->ns;
}
ly_print(out, "%*s<%s xmlns=\"%s\"", LEVEL, INDENT, node->schema->name, ns);
} else {
ly_print(out, "%*s<%s", LEVEL, INDENT, node->schema->name);
}
if (toplevel) {
xml_print_ns(out, node);
}
xml_print_attrs(out, node);
if (!node->child) {
ly_print(out, "/>%s", level ? "\n" : "");
return;
}
ly_print(out, ">%s", level ? "\n" : "");
LY_TREE_FOR(node->child, child) {
xml_print_node(out, level ? level + 1 : 0, child, 0);
}
ly_print(out, "%*s</%s>%s", LEVEL, INDENT, node->schema->name, level ? "\n" : "");
}
static void
xml_print_list(struct lyout *out, int level, const struct lyd_node *node, int is_list, int toplevel)
{
struct lyd_node *child;
const char *ns;
if (is_list) {
/* list print */
if (!node->parent || nscmp(node, node->parent)) {
/* print "namespace" */
if (node->schema->module->type) {
/* submodule, get module */
ns = ((struct lys_submodule *)node->schema->module)->belongsto->ns;
} else {
ns = node->schema->module->ns;
}
ly_print(out, "%*s<%s xmlns=\"%s\"", LEVEL, INDENT, node->schema->name, ns);
} else {
ly_print(out, "%*s<%s", LEVEL, INDENT, node->schema->name);
}
if (toplevel) {
xml_print_ns(out, node);
}
xml_print_attrs(out, node);
if (!node->child) {
ly_print(out, "/>%s", level ? "\n" : "");
return;
}
ly_print(out, ">%s", level ? "\n" : "");
LY_TREE_FOR(node->child, child) {
xml_print_node(out, level ? level + 1 : 0, child, 0);
}
ly_print(out, "%*s</%s>%s", LEVEL, INDENT, node->schema->name, level ? "\n" : "");
} else {
/* leaf-list print */
xml_print_leaf(out, level, node, toplevel);
}
}
static void
xml_print_anyxml(struct lyout *out, int level, const struct lyd_node *node, int UNUSED(toplevel))
{
FILE *stream;
char *buf;
size_t buf_size;
struct lyd_node_anyxml *axml = (struct lyd_node_anyxml *)node;
/*const char *ns;*/
/* anyxml with it's namespace is saved in the value, may change later */
/*if (!node->parent || nscmp(node, node->parent)) {
* print "namespace" *
if (node->schema->module->type) {
* submodule, get module *
ns = ((struct lys_submodule *)node->schema->module)->belongsto->ns;
} else {
ns = node->schema->module->ns;
}
ly_print(out, "%*s<%s xmlns=\"%s\"", LEVEL, INDENT, node->schema->name, ns);
} else {
ly_print(out, "%*s<%s", LEVEL, INDENT, node->schema->name);
}
if (toplevel) {
xml_print_ns(out, node);
}
xml_print_attrs(out, node);
ly_print(out, ">%s", level ? "\n" : "");*/
/* dump the anyxml into a buffer */
stream = open_memstream(&buf, &buf_size);
lyxml_dump_file(stream, axml->value, LYXML_DUMP_FORMAT);
fclose(stream);
ly_print(out, "%*s%s", LEVEL, INDENT, buf);
free(buf);
}
void
xml_print_node(struct lyout *out, int level, const struct lyd_node *node, int toplevel)
{
switch (node->schema->nodetype) {
case LYS_NOTIF:
case LYS_RPC:
case LYS_CONTAINER:
xml_print_container(out, level, node, toplevel);
break;
case LYS_LEAF:
xml_print_leaf(out, level, node, toplevel);
break;
case LYS_LEAFLIST:
xml_print_list(out, level, node, 0, toplevel);
break;
case LYS_LIST:
xml_print_list(out, level, node, 1, toplevel);
break;
case LYS_ANYXML:
xml_print_anyxml(out, level, node, toplevel);
break;
default:
LOGINT;
break;
}
}
int
xml_print_data(struct lyout *out, const struct lyd_node *root, int format, int options)
{
const struct lyd_node *node;
/* content */
LY_TREE_FOR(root, node) {
xml_print_node(out, format ? 1 : 0, node, 1);
if (!(options & LYP_WITHSIBLINGS)) {
break;
}
}
return EXIT_SUCCESS;
}