yin parser: initial version
Many TODOs, just part of functionality, does not validate input file,
just tries to parse it.
diff --git a/src/yin.c b/src/yin.c
new file mode 100644
index 0000000..fb87259
--- /dev/null
+++ b/src/yin.c
@@ -0,0 +1,865 @@
+/**
+ * @file yin.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief YIN parser for libyang
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+#include "common.h"
+#include "context.h"
+#include "dict.h"
+#include "yin.h"
+
+#include "tree_internal.h"
+#include "xml.h"
+
+static struct ly_mnode *read_yin_choice(struct ly_module *, struct ly_mnode *, struct lyxml_elem *);
+static struct ly_mnode *read_yin_container(struct ly_module *, struct ly_mnode *, struct lyxml_elem *);
+static struct ly_mnode *read_yin_leaf(struct ly_module *, struct ly_mnode *, struct lyxml_elem *);
+static struct ly_mnode *read_yin_leaflist(struct ly_module *, struct ly_mnode *, struct lyxml_elem *);
+static struct ly_mnode *read_yin_list(struct ly_module *, struct ly_mnode *, struct lyxml_elem *);
+static struct ly_mnode *read_yin_grouping(struct ly_module *, struct ly_mnode *, struct lyxml_elem *);
+
+static char *read_yin_text(struct ly_ctx *ctx, struct lyxml_elem *node, const char *name)
+{
+ char *value;
+
+ /* there should be <text> child */
+ if (!node->child || !node->child->name
+ || strcmp(node->child->name, "text")) {
+ LY_WRN("Expected \"text\" element in \"%s\" element.", name);
+ } else {
+ value = node->child->content;
+ if (value) {
+ return lydict_insert(ctx, value, strlen(value));
+ }
+ }
+ return NULL;
+}
+
+static struct ly_tpdf *find_superior_type(const char *name,
+ struct ly_module *module,
+ struct ly_mnode *parent)
+{
+ int i, found = 0;
+ int prefix_len = 0;
+ const char *qname;
+
+ qname = strchr(name, ':');
+
+ if (!qname) {
+ /* no prefix, try built-in types */
+ for (i = 1; i < LY_DATA_TYPE_COUNT; i++) {
+ if (!strcmp(ly_types[i].def->name, name)) {
+ return ly_types[i].def;
+ }
+ }
+ qname = name;
+ } else {
+ /* set qname to correct position after colon */
+ prefix_len = qname - name;
+ qname++;
+
+ if (!strncmp(name, module->prefix, prefix_len) && !module->prefix[prefix_len]) {
+ /* prefix refers to the current module, ignore it */
+ prefix_len = 0;
+ }
+ }
+
+ if (!prefix_len && parent) {
+ /* TODO - search in local typedefs */
+ } else if (prefix_len) {
+ /* get module where to search */
+ for (i = 0; i < module->imp_size; i++) {
+ if (!strncmp(module->imp[i].prefix, name, prefix_len) && !module->imp[i].prefix[prefix_len]) {
+ module = module->imp[i].module;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ /* TODO - syntax error */
+ return NULL;
+ }
+ }
+
+ /* search in top level typedefs */
+ for (i = 0; i < module->tpdf_size; i++) {
+ if (!strcmp(module->tpdf[i].name, qname)) {
+ return &module->tpdf[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int fill_yin_type(struct ly_module *module, struct ly_mnode *parent,
+ struct lyxml_elem *yin, struct ly_type *type)
+{
+ const char *value;
+
+ value = lyxml_get_attr(yin, "name", NULL);
+ type->name = lydict_insert(module->ctx, value, strlen(value));
+ type->der = find_superior_type(value, module, parent);
+
+ return EXIT_SUCCESS;
+}
+
+static int fill_yin_typedef(struct ly_module *module, struct ly_mnode *parent,
+ struct lyxml_elem *yin, struct ly_tpdf *tpdf)
+{
+ const char *value;
+ struct lyxml_elem *node, *next;
+
+ value = lyxml_get_attr(yin, "name", NULL);
+ tpdf->name = lydict_insert(module->ctx, value, strlen(value));
+
+ LY_TREE_FOR_SAFE(yin->child, next, node) {
+ if (!strcmp(node->name, "type")) {
+ fill_yin_type(module, parent, node, &tpdf->type);
+ tpdf->base = tpdf->type.der->base;
+ /* optional statements */
+ } else if (!strcmp(node->name, "description")) {
+ tpdf->dsc = read_yin_text(module->ctx, node, "description");
+ lyxml_free_elem(module->ctx, node);
+ } else if (!strcmp(node->name, "reference")) {
+ tpdf->ref = read_yin_text(module->ctx, node, "reference");
+ lyxml_free_elem(module->ctx, node);
+ } else if (!strcmp(node->name, "status")) {
+ value = lyxml_get_attr(node, "value", NULL);
+ if (!strcmp(value, "current")) {
+ tpdf->flags |= LY_NODE_STATUS_CURR;
+ } else if (!strcmp(value, "deprecated")) {
+ tpdf->flags |= LY_NODE_STATUS_DEPRC;
+ } else if (!strcmp(value, "obsolete")) {
+ tpdf->flags |= LY_NODE_STATUS_OBSLT;
+ }
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/*
+ * Covers:
+ * description, reference, config, status,
+ */
+static int read_yin_common(struct ly_module *module, struct ly_mnode *parent,
+ struct ly_mnode *mnode, struct lyxml_elem *xmlnode)
+{
+ const char *value;
+ struct lyxml_elem *sub, *next;
+ struct ly_ctx * const ctx = module->ctx;
+
+ mnode->module = module;
+
+ value = lyxml_get_attr(xmlnode, "name", NULL);
+ mnode->name = lydict_insert(ctx, value, strlen(value));
+
+ /* process local parameters */
+ LY_TREE_FOR_SAFE(xmlnode->child, next, sub) {
+ if (!strcmp(sub->name, "description")) {
+ mnode->dsc = read_yin_text(ctx, sub, "description");
+ } else if (!strcmp(sub->name, "reference")) {
+ mnode->ref = read_yin_text(ctx, sub, "reference");
+ } else if (!strcmp(sub->name, "config")) {
+ value = lyxml_get_attr(sub, "value", NULL);
+ if (!strcmp(value, "false")) {
+ mnode->flags |= LY_NODE_CONFIG_R;
+ } else if (!strcmp(value, "false")) {
+ mnode->flags |= LY_NODE_CONFIG_W;
+ }
+ } else if (!strcmp(sub->name, "status")) {
+ value = lyxml_get_attr(sub, "value", NULL);
+ if (!strcmp(value, "current")) {
+ mnode->flags |= LY_NODE_STATUS_CURR;
+ } else if (!strcmp(value, "deprecated")) {
+ mnode->flags |= LY_NODE_STATUS_DEPRC;
+ } else if (!strcmp(value, "obsolete")) {
+ mnode->flags |= LY_NODE_STATUS_OBSLT;
+ }
+ } else {
+ /* skip the lyxml_free_elem */
+ continue;
+ }
+ lyxml_free_elem(ctx, sub);
+ }
+
+ if (mnode->nodetype != LY_NODE_GROUPING && !(mnode->flags & LY_NODE_CONFIG_MASK)) {
+ /* get config flag from parent */
+ if (parent) {
+ mnode->flags |= parent->flags & LY_NODE_CONFIG_MASK;
+ } else {
+ /* default config is true */
+ mnode->flags |= LY_NODE_CONFIG_W;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static struct ly_mnode *read_yin_choice(struct ly_module *module,
+ struct ly_mnode *parent,
+ struct lyxml_elem *node)
+{
+ struct lyxml_elem *sub, *next;
+ struct ly_ctx * const ctx = module->ctx;
+ struct ly_mnode *retval;
+ struct ly_mnode_choice *choice;
+
+ if (!module || !node) {
+ ly_errno = LY_EINVAL;
+ return NULL;
+ }
+
+ choice = calloc(1, sizeof *choice);
+ choice->nodetype = LY_NODE_CHOICE;
+ choice->module = module;
+ choice->prev = (struct ly_mnode *)choice;
+ retval = (struct ly_mnode *)choice;
+
+ if (read_yin_common(module, parent, retval, node)) {
+ goto error;
+ }
+
+ /* process choice's specific children */
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ if (!strcmp(sub->name, "container")) {
+ read_yin_container(module, retval, sub);
+ } else if (!strcmp(sub->name, "leaf-list")) {
+ read_yin_leaflist(module, retval, sub);
+ } else if (!strcmp(sub->name, "leaf")) {
+ read_yin_leaf(module, retval, sub);
+ } else if (!strcmp(sub->name, "list")) {
+ read_yin_list(module, retval, sub);
+ }
+ lyxml_free_elem(ctx, sub);
+ }
+
+ ly_mnode_addchild(parent, retval);
+
+ return retval;
+
+error:
+
+ ly_mnode_free(retval);
+
+ return NULL;
+}
+
+static struct ly_mnode *read_yin_leaf(struct ly_module *module,
+ struct ly_mnode *parent,
+ struct lyxml_elem *node)
+{
+ struct ly_mnode *retval;
+ struct ly_mnode_leaf *leaf;
+ struct lyxml_elem *sub, *next;
+
+ if (!module || !node) {
+ ly_errno = LY_EINVAL;
+ return NULL;
+ }
+
+ leaf = calloc(1, sizeof *leaf);
+ leaf->nodetype = LY_NODE_LEAF;
+ leaf->prev = (struct ly_mnode *)leaf;
+ retval = (struct ly_mnode *)leaf;
+
+ if (read_yin_common(module, parent, retval, node)) {
+ goto error;
+ }
+
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ if (!strcmp(sub->name, "type")) {
+ fill_yin_type(module, parent, sub, &leaf->type);
+ }
+ }
+
+ ly_mnode_addchild(parent, retval);
+
+ return retval;
+
+error:
+
+ ly_mnode_free(retval);
+
+ return NULL;
+}
+
+static struct ly_mnode *read_yin_leaflist(struct ly_module *module,
+ struct ly_mnode *parent,
+ struct lyxml_elem *node)
+{
+ struct ly_mnode *retval;
+ struct ly_mnode_leaflist *llist;
+ struct lyxml_elem *sub, *next;
+
+ if (!module || !node) {
+ ly_errno = LY_EINVAL;
+ return NULL;
+ }
+
+ llist = calloc(1, sizeof *llist);
+ llist->nodetype = LY_NODE_LEAFLIST;
+ llist->prev = (struct ly_mnode *)llist;
+ retval = (struct ly_mnode *)llist;
+
+ if (read_yin_common(module, parent, retval, node)) {
+ goto error;
+ }
+
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ if (!strcmp(sub->name, "type")) {
+ fill_yin_type(module, parent, sub, &llist->type);
+ }
+ }
+
+ ly_mnode_addchild(parent, retval);
+
+ return retval;
+
+error:
+
+ ly_mnode_free(retval);
+
+ return NULL;
+}
+
+static struct ly_mnode *read_yin_list(struct ly_module *module,
+ struct ly_mnode *parent,
+ struct lyxml_elem *node)
+{
+ struct ly_mnode *retval, *mnode;
+ struct ly_mnode_list *list;
+ struct lyxml_elem *sub, *next, root = {0};
+ int c_tpdf = 0;
+
+ if (!module || !node) {
+ ly_errno = LY_EINVAL;
+ return NULL;
+ }
+
+ list = calloc(1, sizeof *list);
+ list->nodetype = LY_NODE_LIST;
+ list->prev = (struct ly_mnode *)list;
+ retval = (struct ly_mnode *)list;
+
+ if (read_yin_common(module, parent, retval, node)) {
+ goto error;
+ }
+
+ /* process list's specific children */
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ /* data statements */
+ if (!strcmp(sub->name, "container") ||
+ !strcmp(sub->name, "leaf-list") ||
+ !strcmp(sub->name, "leaf") ||
+ !strcmp(sub->name, "list") ||
+ !strcmp(sub->name, "choice") ||
+ !strcmp(sub->name, "grouping")) {
+ lyxml_unlink_elem(sub);
+ lyxml_add_child(&root, sub);
+
+ /* array counters */
+ } else if (!strcmp(sub->name, "typedef")) {
+ c_tpdf++;
+ }
+ }
+
+ /* middle part - process nodes with cardinality of 0..n except the data nodes */
+ if (c_tpdf) {
+ list->tpdf_size = c_tpdf;
+ list->tpdf = calloc(c_tpdf, sizeof *list->tpdf);
+ c_tpdf = 0;
+ }
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ if (!strcmp(sub->name, "typedef")) {
+ fill_yin_typedef(module, retval, sub, &list->tpdf[c_tpdf]);
+ c_tpdf++;
+ }
+
+ lyxml_free_elem(module->ctx, sub);
+ }
+
+ /* last part - process data nodes */
+ LY_TREE_FOR_SAFE(root.child, next, sub) {
+ if (!strcmp(sub->name, "container")) {
+ mnode = read_yin_container(module, retval, sub);
+ } else if (!strcmp(sub->name, "leaf-list")) {
+ mnode = read_yin_leaflist(module, retval, sub);
+ } else if (!strcmp(sub->name, "leaf")) {
+ mnode = read_yin_leaf(module, retval, sub);
+ } else if (!strcmp(sub->name, "list")) {
+ mnode = read_yin_list(module, retval, sub);
+ } else if (!strcmp(sub->name, "choice")) {
+ mnode = read_yin_choice(module, retval, sub);
+ } else if (!strcmp(sub->name, "grouping")) {
+ mnode = read_yin_grouping(module, retval, sub);
+ } else {
+ continue;
+ }
+ lyxml_free_elem(module->ctx, sub);
+
+ if (mnode) {
+ /* add child */
+ ly_mnode_addchild(retval, mnode);
+ mnode = NULL;
+ }
+ }
+
+ ly_mnode_addchild(parent, retval);
+
+ return retval;
+
+error:
+
+ ly_mnode_free(retval);
+
+ return NULL;
+}
+
+static struct ly_mnode *read_yin_container(struct ly_module *module,
+ struct ly_mnode *parent,
+ struct lyxml_elem *node)
+{
+ struct lyxml_elem *sub, *next, root = {0};
+ struct ly_mnode *mnode = NULL;
+ struct ly_mnode *retval;
+ struct ly_mnode_container *cont;
+ int c_tpdf = 0;
+
+ if (!module || !node) {
+ ly_errno = LY_EINVAL;
+ return NULL;
+ }
+
+ cont = calloc(1, sizeof *cont);
+ cont->nodetype = LY_NODE_CONTAINER;
+ cont->prev = (struct ly_mnode *)cont;
+ retval = (struct ly_mnode *)cont;
+
+ if (read_yin_common(module, parent, retval, node)) {
+ goto error;
+ }
+
+ /* process container's specific children */
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ /* data statements */
+ if (!strcmp(sub->name, "container") ||
+ !strcmp(sub->name, "leaf-list") ||
+ !strcmp(sub->name, "leaf") ||
+ !strcmp(sub->name, "list") ||
+ !strcmp(sub->name, "choice") ||
+ !strcmp(sub->name, "grouping")) {
+ lyxml_unlink_elem(sub);
+ lyxml_add_child(&root, sub);
+
+ /* array counters */
+ } else if (!strcmp(sub->name, "typedef")) {
+ c_tpdf++;
+ }
+ }
+
+ /* middle part - process nodes with cardinality of 0..n except the data nodes */
+ if (c_tpdf) {
+ cont->tpdf_size = c_tpdf;
+ cont->tpdf = calloc(c_tpdf, sizeof *cont->tpdf);
+ c_tpdf = 0;
+ }
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ if (!strcmp(sub->name, "typedef")) {
+ fill_yin_typedef(module, retval, sub, &cont->tpdf[c_tpdf]);
+ c_tpdf++;
+ }
+
+ lyxml_free_elem(module->ctx, sub);
+ }
+
+ /* last part - process data nodes */
+ LY_TREE_FOR_SAFE(root.child, next, sub) {
+ if (!strcmp(sub->name, "container")) {
+ mnode = read_yin_container(module, retval, sub);
+ } else if (!strcmp(sub->name, "leaf-list")) {
+ mnode = read_yin_leaflist(module, retval, sub);
+ } else if (!strcmp(sub->name, "leaf")) {
+ mnode = read_yin_leaf(module, retval, sub);
+ } else if (!strcmp(sub->name, "list")) {
+ mnode = read_yin_list(module, retval, sub);
+ } else if (!strcmp(sub->name, "choice")) {
+ mnode = read_yin_choice(module, retval, sub);
+ } else if (!strcmp(sub->name, "grouping")) {
+ mnode = read_yin_grouping(module, retval, sub);
+ } else {
+ continue;
+ }
+ lyxml_free_elem(module->ctx, sub);
+
+ if (mnode) {
+ /* add child */
+ ly_mnode_addchild(retval, mnode);
+ mnode = NULL;
+ }
+ }
+
+ ly_mnode_addchild(parent, retval);
+
+ return retval;
+
+error:
+
+ ly_mnode_free(retval);
+
+ return NULL;
+}
+
+static struct ly_mnode *read_yin_grouping(struct ly_module *module,
+ struct ly_mnode *parent,
+ struct lyxml_elem *node)
+{
+ struct lyxml_elem *sub, *next, root = {0};
+ struct ly_mnode *mnode = NULL;
+ struct ly_mnode *retval;
+ struct ly_mnode_grp *grp;
+ int c_tpdf = 0;
+
+ grp = calloc(1, sizeof *grp);
+ grp->nodetype = LY_NODE_GROUPING;
+ grp->module = module;
+ grp->prev = (struct ly_mnode *)grp;
+ retval = (struct ly_mnode *)grp;
+
+ if (read_yin_common(module, parent, retval, node)) {
+ goto error;
+ }
+
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ /* data statements */
+ if (!strcmp(sub->name, "container") ||
+ !strcmp(sub->name, "leaf-list") ||
+ !strcmp(sub->name, "leaf") ||
+ !strcmp(sub->name, "list") ||
+ !strcmp(sub->name, "choice") ||
+ !strcmp(sub->name, "grouping")) {
+ lyxml_unlink_elem(sub);
+ lyxml_add_child(&root, sub);
+
+ /* array counters */
+ } else if (!strcmp(sub->name, "typedef")) {
+ c_tpdf++;
+ }
+ }
+
+ /* middle part - process nodes with cardinality of 0..n except the data nodes */
+ if (c_tpdf) {
+ grp->tpdf_size = c_tpdf;
+ grp->tpdf = calloc(c_tpdf, sizeof *grp->tpdf);
+ c_tpdf = 0;
+ }
+ LY_TREE_FOR_SAFE(node->child, next, sub) {
+ if (!strcmp(sub->name, "typedef")) {
+ fill_yin_typedef(module, retval, sub, &grp->tpdf[c_tpdf]);
+ c_tpdf++;
+ }
+
+ lyxml_free_elem(module->ctx, sub);
+ }
+
+ /* last part - process data nodes */
+ LY_TREE_FOR_SAFE(root.child, next, sub) {
+ if (!strcmp(sub->name, "container")) {
+ mnode = read_yin_container(module, retval, sub);
+ } else if (!strcmp(sub->name, "leaf-list")) {
+ mnode = read_yin_leaflist(module, retval, sub);
+ } else if (!strcmp(sub->name, "leaf")) {
+ mnode = read_yin_leaf(module, retval, sub);
+ } else if (!strcmp(sub->name, "list")) {
+ mnode = read_yin_list(module, retval, sub);
+ } else if (!strcmp(sub->name, "choice")) {
+ mnode = read_yin_choice(module, retval, sub);
+ } else if (!strcmp(sub->name, "grouping")) {
+ mnode = read_yin_grouping(module, retval, sub);
+ } else {
+ continue;
+ }
+ lyxml_free_elem(module->ctx, sub);
+
+ if (mnode) {
+ /* add child */
+ ly_mnode_addchild(retval, mnode);
+ mnode = NULL;
+ }
+ }
+
+ ly_mnode_addchild(parent, retval);
+
+ return retval;
+
+error:
+
+ ly_mnode_free(retval);
+
+ return NULL;
+}
+
+struct ly_module *ly_read_yin(struct ly_ctx *ctx, const char *data)
+{
+ struct lyxml_elem *yin, *node, *next, *child, root = {0};
+ struct ly_module *module = NULL, **newlist = NULL, *imp;
+ struct ly_mnode *ynode = NULL;
+ const char *value;
+ int i;
+ /* counters */
+ int c_imp = 0, c_rev = 0, c_tpdf = 0;
+
+ yin = lyxml_read(ctx, data, 0);
+ if (!yin) {
+ return NULL;
+ }
+
+ /* check root element */
+ if (!yin->name || strcmp(yin->name, "module")) {
+ /* TODO: support submodules */
+ LY_ERR(LY_EVALID, "Expected \"module\" element, but have \"%s\".");
+ goto error;
+ }
+
+ /* check its namespace */
+ if (!yin->ns || !yin->ns->value || strcmp(yin->ns->value, LY_NSYIN)) {
+ LY_ERR(LY_EVALID,
+ "Invalid namespace of the \"module\" element, \"" LY_NSYIN
+ "\" expected.");
+ goto error;
+ }
+
+ value = lyxml_get_attr(yin, "name", NULL);
+ if (!value) {
+ LY_ERR(LY_EVALID, "Missing \"name\" attribute of the \"module\".");
+ goto error;
+ }
+
+ module = calloc(1, sizeof *module);
+ if (!module) {
+ ly_errno = LY_EFATAL;
+ goto error;
+ }
+
+ module->ctx = ctx;
+ module->name = lydict_insert(ctx, value, strlen(value));
+
+ LY_VRB("reading module %s", module->name);
+
+ /*
+ * in the first run, we process elements with cardinality of 1 or 0..1 and
+ * count elements with cardinality 0..n. Data elements (choices, containers,
+ * leafs, lists, leaf-lists) are moved aside to be processed last, since we
+ * need have all top-level and groupings already prepared at that time. In
+ * the middle loop, we process other elements with carinality of 0..n since
+ * we need to allocate arrays to store them.
+ */
+ LY_TREE_FOR_SAFE(yin->child, next, node) {
+ if (!node->ns || strcmp(node->ns->value, LY_NSYIN)) {
+ lyxml_free_elem(ctx, node);
+ continue;
+ }
+
+ if (!strcmp(node->name, "namespace")) {
+ value = lyxml_get_attr(node, "uri", NULL);
+ if (!value) {
+ LY_ERR(LY_EVALID,
+ "%s: Missing \"uri\" attribute in \"namespace\" element.", module->name);
+ goto error;
+ }
+ module->ns = lydict_insert(ctx, value, strlen(value));
+ lyxml_free_elem(ctx, node);
+ } else if (!strcmp(node->name, "prefix")) {
+ value = lyxml_get_attr(node, "value", NULL);
+ if (!value) {
+ LY_ERR(LY_EVALID,
+ "%s: Missing \"value\" attribute in \"prefix\" element.", module->name);
+ goto error;
+ }
+ module->prefix = lydict_insert(ctx, value, strlen(value));
+ lyxml_free_elem(ctx, node);
+ } else if (!strcmp(node->name, "import")) {
+ c_imp++;
+ } else if (!strcmp(node->name, "revision")) {
+ c_rev++;
+ } else if (!strcmp(node->name, "typedef")) {
+ c_tpdf++;
+
+ /* data statements */
+ } else if (!strcmp(node->name, "container") ||
+ !strcmp(node->name, "leaf-list") ||
+ !strcmp(node->name, "leaf") ||
+ !strcmp(node->name, "list") ||
+ !strcmp(node->name, "choice") ||
+ !strcmp(node->name, "grouping")) {
+ lyxml_unlink_elem(node);
+ lyxml_add_child(&root, node);
+
+ /* optional statements */
+ } else if (!strcmp(node->name, "description")) {
+ module->dsc = read_yin_text(ctx, node, "description");
+ lyxml_free_elem(ctx, node);
+ } else if (!strcmp(node->name, "reference")) {
+ module->ref = read_yin_text(ctx, node, "reference");
+ lyxml_free_elem(ctx, node);
+ } else if (!strcmp(node->name, "organization")) {
+ module->org = read_yin_text(ctx, node, "organization");
+ lyxml_free_elem(ctx, node);
+ } else if (!strcmp(node->name, "contact")) {
+ module->contact = read_yin_text(ctx, node, "contact");
+ lyxml_free_elem(ctx, node);
+ } else if (!strcmp(node->name, "yang-version")) {
+ /* TODO: support YANG 1.1 */
+ value = lyxml_get_attr(node, "value", NULL);
+ if (strcmp(value, "1")) {
+ LY_ERR(LY_EVALID, "%s: Invalid \"yang-version\" value.", module->name);
+ goto error;
+ }
+ module->version = 0;
+ lyxml_free_elem(ctx, node);
+ }
+ }
+
+ /* allocate arrays for elements with cardinality of 0..n */
+ if (c_imp) {
+ module->imp_size = c_imp;
+ module->imp = calloc(c_imp, sizeof *module->imp);
+ c_imp = 0;
+ }
+ if (c_rev) {
+ module->rev_size = c_rev;
+ module->rev = calloc(c_rev, sizeof *module->rev);
+ c_rev = 0;
+ }
+ if (c_tpdf) {
+ module->tpdf_size = c_tpdf;
+ module->tpdf = calloc(c_tpdf, sizeof *module->tpdf);
+ c_tpdf = 0;
+ }
+
+ /* middle part - process nodes with cardinality of 0..n except the data nodes */
+ LY_TREE_FOR_SAFE(yin->child, next, node) {
+ if (!strcmp(node->name, "import")) {
+ LY_TREE_FOR(node->child, child) {
+ if (!strcmp(child->name, "prefix")) {
+ value = lyxml_get_attr(child, "value", NULL);
+ module->imp[c_imp].prefix = lydict_insert(ctx, value, strlen(value));
+ } else if (!strcmp(child->name, "revision-date")) {
+ value = lyxml_get_attr(child, "date", NULL);
+ memcpy(module->imp[c_imp].rev,
+ lyxml_get_attr(child, "date", NULL),
+ LY_REV_SIZE - 1);
+ }
+ }
+ value = lyxml_get_attr(node, "module", NULL);
+ imp = ly_ctx_get_model(ctx, value, module->imp[c_imp].rev[0] ? module->imp[c_imp].rev : NULL);
+ if (!imp) {
+ LY_ERR(LY_EVALID, "Importing \"%s\" module into \"%s\" failed.",
+ value, module->name);
+ goto error;
+ }
+ module->imp[c_imp].module = imp;
+ c_imp++;
+ } else if (!strcmp(node->name, "revision")) {
+ memcpy(module->rev[c_rev].date,
+ lyxml_get_attr(node, "date", NULL), LY_REV_SIZE - 1);
+ LY_TREE_FOR(node->child, child) {
+ if (!strcmp(child->name, "description")) {
+ module->rev[c_rev].dsc = read_yin_text(ctx, child, "description");
+ } else if (!strcmp(child->name, "reference")) {
+ module->rev[c_rev].ref = read_yin_text(ctx, child, "reference");
+ }
+ }
+ c_rev++;
+ } else if (!strcmp(node->name, "typedef")) {
+ fill_yin_typedef(module, NULL, node, &module->tpdf[c_tpdf]);
+ c_tpdf++;
+ }
+
+ lyxml_free_elem(ctx, node);
+ }
+
+ /* last part - process data nodes */
+ LY_TREE_FOR_SAFE(root.child, next, node) {
+
+ if (!strcmp(node->name, "container")) {
+ ynode = read_yin_container(module, NULL, node);
+ } else if (!strcmp(node->name, "leaf-list")) {
+ ynode = read_yin_leaflist(module, NULL, node);
+ } else if (!strcmp(node->name, "leaf")) {
+ ynode = read_yin_leaf(module, NULL, node);
+ } else if (!strcmp(node->name, "list")) {
+ ynode = read_yin_list(module, NULL, node);
+ } else if (!strcmp(node->name, "choice")) {
+ ynode = read_yin_choice(module, NULL, node);
+ } else if (!strcmp(node->name, "grouping")) {
+ ynode = read_yin_grouping(module, NULL, node);
+ } else {
+ continue;
+ }
+ lyxml_free_elem(ctx, node);
+
+ if (ynode) {
+ /* include data element */
+ if (module->data) {
+ module->data->prev->next = ynode;
+ ynode->prev = module->data->prev;
+ module->data->prev = ynode;
+ } else {
+ module->data = ynode;
+ }
+ ynode = NULL;
+ }
+ }
+
+ /* add to the context's list of modules */
+ if (ctx->models.used == ctx->models.size) {
+ newlist = realloc(ctx->models.list, ctx->models.size * 2);
+ if (!newlist) {
+ LY_ERR(LY_EFATAL, NULL);
+ goto error;
+ }
+ for (i = ctx->models.size; i < ctx->models.size * 2; i++) {
+ newlist[i] = NULL;
+ }
+ ctx->models.size *= 2;
+ ctx->models.list = newlist;
+ }
+ for (i = 0; ctx->models.list[i]; i++);
+ ctx->models.list[i] = module;
+ ctx->models.used++;
+
+ /* cleanup */
+ lyxml_free_elem(ctx, yin);
+
+ LY_VRB("module %s successfully parsed", module->name);
+
+ return module;
+
+error:
+ /* cleanup */
+ lyxml_free_elem(ctx, yin);
+ ly_model_free(module);
+
+ return NULL;
+}