| /** |
| * @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 <ctype.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stddef.h> |
| |
| #include "libyang.h" |
| #include "common.h" |
| #include "context.h" |
| #include "dict.h" |
| #include "yin.h" |
| |
| #include "tree_internal.h" |
| #include "xml.h" |
| |
| static int read_yin_common(struct ly_module *, struct ly_mnode *, struct ly_mnode *, struct lyxml_elem *, int ); |
| 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; |
| struct ly_tpdf *tpdf; |
| int tpdf_size; |
| |
| 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) { |
| /* search in local typedefs */ |
| while (parent) { |
| switch (parent->nodetype) { |
| case LY_NODE_CONTAINER: |
| tpdf_size = ((struct ly_mnode_container *)parent)->tpdf_size; |
| tpdf = ((struct ly_mnode_container *)parent)->tpdf; |
| break; |
| case LY_NODE_LIST: |
| tpdf_size = ((struct ly_mnode_list *)parent)->tpdf_size; |
| tpdf = ((struct ly_mnode_list *)parent)->tpdf; |
| break; |
| case LY_NODE_GROUPING: |
| tpdf_size = ((struct ly_mnode_grp *)parent)->tpdf_size; |
| tpdf = ((struct ly_mnode_grp *)parent)->tpdf; |
| break; |
| default: |
| parent = parent->parent; |
| continue; |
| } |
| |
| for (i = 0; i < tpdf_size; i++) { |
| if (!strcmp(tpdf[i].name, qname)) { |
| return &tpdf[i]; |
| } |
| } |
| |
| parent = parent->parent; |
| } |
| } 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, *delim; |
| struct lyxml_elem *next, *node, root = {0}; |
| int i, j, r; |
| int64_t v, v_; |
| |
| value = lyxml_get_attr(yin, "name", NULL); |
| delim = strchr(value, ':'); |
| if (delim) { |
| type->prefix = lydict_insert(module->ctx, value, delim - value); |
| } |
| type->der = find_superior_type(value, module, parent); |
| /* TODO error */ |
| type->base = type->der->type.base; |
| |
| switch (type->base) { |
| case LY_TYPE_BINARY: |
| /* length, 9.4.4 |
| * - optional, 0..1, rekurzivni - omezuje, string (podobne jako range), hodnoty se musi vejit do 64b, podelementy |
| */ |
| break; |
| case LY_TYPE_BITS: |
| /* bit, 9.7.4 |
| * 1..n, nerekurzivni, stringy s podelementy */ |
| break; |
| case LY_TYPE_DEC64: |
| /* fraction-digits, 9.3.4 |
| * - MUST, 1, nerekurzivni, hodnota 1-18 */ |
| /* range, 9.2.4 |
| * - optional, 0..1, rekurzivne - omezuje, string, podelementy*/ |
| break; |
| case LY_TYPE_ENUM: |
| /* RFC 6020 9.6.4 */ |
| if (type->der->module) { |
| ly_verr(LY_VERR_BAD_RESTR, "enum"); |
| goto error; |
| } |
| /* get enum specification, at least one must be present */ |
| LY_TREE_FOR_SAFE(yin->child, next, node) { |
| if (!strcmp(node->name, "enum")) { |
| lyxml_unlink_elem(node); |
| lyxml_add_child(&root, node); |
| type->info.enums.count++; |
| } |
| } |
| if (yin->child) { |
| ly_verr(LY_VERR_UNEXP_STMT, yin->child->name); |
| goto error; |
| } |
| if (!type->info.enums.count) { |
| if (type->der->type.der) { |
| /* this is just a derived type with no enum specified */ |
| break; |
| } |
| ly_verr(LY_VERR_MISS_STMT2, "enum", "type"); |
| goto error; |
| } |
| |
| type->info.enums.list = calloc(type->info.enums.count, sizeof *type->info.enums.list); |
| for (i = v = 0; root.child; i++) { |
| r = read_yin_common(module, NULL, (struct ly_mnode *)&type->info.enums.list[i], root.child, 0); |
| if (r) { |
| type->info.enums.count = i + 1; |
| goto error; |
| } |
| /* the assigned name MUST NOT have any leading or trailing whitespace characters */ |
| value = type->info.enums.list[i].name; |
| if (isspace(value[0]) || isspace(value[strlen(value) - 1])) { |
| ly_verr(LY_VERR_ENUM_WS, value); |
| type->info.enums.count = i + 1; |
| goto error; |
| } |
| |
| /* check the name uniqueness */ |
| for (j = 0; j < i; j++) { |
| if (!strcmp(type->info.enums.list[j].name, type->info.enums.list[i].name)) { |
| ly_verr(LY_VERR_ENUM_DUP_NAME, type->info.enums.list[i].name); |
| type->info.enums.count = i + 1; |
| goto error; |
| } |
| } |
| |
| node = root.child->child; |
| if (node && !strcmp(node->name, "value")) { |
| value = lyxml_get_attr(node, "value", NULL); |
| v_ = strtol(value, NULL, 10); |
| |
| /* range check */ |
| if (v_ < INT32_MIN || v_ > INT32_MAX) { |
| ly_verr(LY_VERR_UNEXP_VAL, value, "enum/value"); |
| type->info.enums.count = i + 1; |
| goto error; |
| } |
| type->info.enums.list[i].value = v_; |
| |
| /* keep the highest enum value for automatic increment */ |
| if (type->info.enums.list[i].value > v) { |
| v = type->info.enums.list[i].value; |
| v++; |
| } else { |
| /* check that the value is unique */ |
| for (j = 0; j < i; j++) { |
| if (type->info.enums.list[j].value == type->info.enums.list[i].value) { |
| ly_verr(LY_VERR_ENUM_DUP_VAL, type->info.enums.list[i].value, type->info.enums.list[i].name); |
| type->info.enums.count = i + 1; |
| goto error; |
| } |
| } |
| } |
| } else { |
| /* assign value automatically */ |
| if (v > INT32_MAX) { |
| ly_verr(LY_VERR_UNEXP_VAL, "2147483648", "enum/value"); |
| type->info.enums.count = i + 1; |
| goto error; |
| } |
| type->info.enums.list[i].value = v; |
| v++; |
| } |
| lyxml_free_elem(module->ctx, root.child); |
| } |
| break; |
| case LY_TYPE_IDENT: |
| /* base, 9.10.2 |
| * - 1, nerekurzivni. string */ |
| break; |
| case LY_TYPE_INST: |
| /* require-instance, 9.13.2 |
| * - 0..1, true/false */ |
| break; |
| 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: |
| /* range, 9.2.4 |
| * - optional, 0..1, i rekurzivne - omezuje, string, podelementy*/ |
| break; |
| case LY_TYPE_LEAFREF: |
| /* path, 9.9.2 |
| * - 1, nerekurzivni, string */ |
| break; |
| case LY_TYPE_STRING: |
| /* length, 9.4.4 |
| * - optional, 0..1, rekurzivni - omezuje, string (podobne jako range), hodnoty se musi vejit do 64b, podelementy |
| * pattern, 9.4.6 |
| * - optional, 0..n, rekurzivni - rozsiruje, string, podelementy */ |
| break; |
| case LY_TYPE_UNION: |
| /* type, 7.4 |
| * - 1..n, nerekurzivni, resp rekurzivni pro union ale bez vazby na predky, nesmi byt empty nebo leafref */ |
| break; |
| default: |
| /* nothing needed : |
| * LY_TYPE_BOOL, LY_TYPE_EMPTY |
| */ |
| break; |
| } |
| |
| return EXIT_SUCCESS; |
| |
| error: |
| |
| while(root.child) { |
| lyxml_free_elem(module->ctx, root.child); |
| } |
| |
| return EXIT_FAILURE; |
| } |
| |
| 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; |
| int r; |
| |
| 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")) { |
| r = fill_yin_type(module, parent, node, &tpdf->type); |
| |
| /* optional statements */ |
| } else if (!strcmp(node->name, "description")) { |
| tpdf->dsc = read_yin_text(module->ctx, node, "description"); |
| if (!tpdf->dsc) { |
| r = 1; |
| } |
| } else if (!strcmp(node->name, "reference")) { |
| tpdf->ref = read_yin_text(module->ctx, node, "reference"); |
| if (!tpdf->dsc) { |
| r = 1; |
| } |
| } 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; |
| } else { |
| ly_verr(LY_VERR_UNEXP_VAL, value, "status"); |
| r = 1; |
| } |
| } |
| lyxml_free_elem(module->ctx, node); |
| if (r) { |
| return EXIT_FAILURE; |
| } |
| } |
| |
| if (!tpdf->type.der) { |
| ly_verr(LY_VERR_MISS_STMT2, "type", "typedef"); |
| return EXIT_FAILURE; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /* |
| * Covers: |
| * description, reference, status, optionaly config |
| */ |
| static int read_yin_common(struct ly_module *module, struct ly_mnode *parent, |
| struct ly_mnode *mnode, struct lyxml_elem *xmlnode, int ext) |
| { |
| const char *value; |
| struct lyxml_elem *sub, *next; |
| struct ly_ctx * const ctx = module->ctx; |
| |
| if (ext) { |
| mnode->module = module; |
| } |
| |
| value = lyxml_get_attr(xmlnode, "name", NULL); |
| mnode->name = lydict_insert(ctx, value, strlen(value)); |
| if (!mnode->name || !mnode->name[0]) { |
| ly_verr(LY_VERR_MISS_ARG, "name", xmlnode->name); |
| return EXIT_FAILURE; |
| } |
| |
| /* 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, "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 if (ext && !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 { |
| /* skip the lyxml_free_elem */ |
| continue; |
| } |
| lyxml_free_elem(ctx, sub); |
| } |
| |
| if (ext && !(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, *r; |
| 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, 1)) { |
| goto error; |
| } |
| |
| /* process choice's specific children */ |
| LY_TREE_FOR_SAFE(node->child, next, sub) { |
| if (!strcmp(sub->name, "container")) { |
| r = read_yin_container(module, retval, sub); |
| } else if (!strcmp(sub->name, "leaf-list")) { |
| r = read_yin_leaflist(module, retval, sub); |
| } else if (!strcmp(sub->name, "leaf")) { |
| r = read_yin_leaf(module, retval, sub); |
| } else if (!strcmp(sub->name, "list")) { |
| r = read_yin_list(module, retval, sub); |
| } |
| lyxml_free_elem(ctx, sub); |
| if (!r) { |
| goto error; |
| } |
| } |
| |
| 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; |
| int r; |
| |
| 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, 1)) { |
| goto error; |
| } |
| |
| LY_TREE_FOR_SAFE(node->child, next, sub) { |
| if (!strcmp(sub->name, "type")) { |
| r = fill_yin_type(module, parent, sub, &leaf->type); |
| if (r) { |
| goto error; |
| } |
| } |
| } |
| |
| 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; |
| int r; |
| |
| 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, 1)) { |
| goto error; |
| } |
| |
| LY_TREE_FOR_SAFE(node->child, next, sub) { |
| if (!strcmp(sub->name, "type")) { |
| r = fill_yin_type(module, parent, sub, &llist->type); |
| if (r) { |
| goto error; |
| } |
| } |
| } |
| |
| 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 r; |
| 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, 1)) { |
| 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")) { |
| r = fill_yin_typedef(module, retval, sub, &list->tpdf[c_tpdf]); |
| c_tpdf++; |
| |
| if (r) { |
| list->tpdf_size = c_tpdf; |
| goto error; |
| } |
| } |
| |
| 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 { |
| /* TODO error */ |
| continue; |
| } |
| lyxml_free_elem(module->ctx, sub); |
| |
| if (!mnode) { |
| goto error; |
| } |
| } |
| |
| ly_mnode_addchild(parent, retval); |
| |
| return retval; |
| |
| error: |
| |
| ly_mnode_free(retval); |
| while(root.child) { |
| lyxml_free_elem(module->ctx, root.child); |
| } |
| |
| 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 r; |
| 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, 1)) { |
| 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")) { |
| r = fill_yin_typedef(module, retval, sub, &cont->tpdf[c_tpdf]); |
| c_tpdf++; |
| |
| if (r) { |
| cont->tpdf_size = c_tpdf; |
| goto error; |
| } |
| } |
| |
| 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 { |
| /* TODO error */ |
| continue; |
| } |
| lyxml_free_elem(module->ctx, sub); |
| |
| if (!mnode) { |
| goto error; |
| } |
| } |
| |
| ly_mnode_addchild(parent, retval); |
| |
| return retval; |
| |
| error: |
| |
| ly_mnode_free(retval); |
| while (root.child) { |
| lyxml_free_elem(module->ctx, root.child); |
| } |
| |
| 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 r; |
| 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, 0)) { |
| 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")) { |
| r = fill_yin_typedef(module, retval, sub, &grp->tpdf[c_tpdf]); |
| c_tpdf++; |
| |
| if (r) { |
| grp->tpdf_size = c_tpdf; |
| goto error; |
| } |
| } |
| |
| 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 { |
| /* TODO error */ |
| continue; |
| } |
| lyxml_free_elem(module->ctx, sub); |
| |
| if (!mnode) { |
| goto error; |
| } |
| } |
| |
| ly_mnode_addchild(parent, retval); |
| |
| return retval; |
| |
| error: |
| |
| ly_mnode_free(retval); |
| while (root.child) { |
| lyxml_free_elem(module->ctx, root.child); |
| } |
| |
| 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 *mnode = NULL; |
| const char *value; |
| int r; |
| 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_verr(LY_VERR_UNEXP_STMT, yin->name); |
| 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")) { |
| if (module->dsc) { |
| ly_verr(LY_VERR_TOOMANY, "description", "module"); |
| goto error; |
| } |
| module->dsc = read_yin_text(ctx, node, "description"); |
| lyxml_free_elem(ctx, node); |
| } else if (!strcmp(node->name, "reference")) { |
| if (module->ref) { |
| ly_verr(LY_VERR_TOOMANY, "reference", "module"); |
| goto error; |
| } |
| module->ref = read_yin_text(ctx, node, "reference"); |
| lyxml_free_elem(ctx, node); |
| } else if (!strcmp(node->name, "organization")) { |
| if (module->org) { |
| ly_verr(LY_VERR_TOOMANY, "organization", "module"); |
| goto error; |
| } |
| module->org = read_yin_text(ctx, node, "organization"); |
| lyxml_free_elem(ctx, node); |
| } else if (!strcmp(node->name, "contact")) { |
| if (module->contact) { |
| ly_verr(LY_VERR_TOOMANY, "contact", "module"); |
| goto error; |
| } |
| 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 */ |
| if (module->version) { |
| ly_verr(LY_VERR_TOOMANY, "yang-version", "module"); |
| goto error; |
| } |
| value = lyxml_get_attr(node, "value", NULL); |
| if (strcmp(value, "1")) { |
| ly_verr(LY_VERR_UNEXP_VAL, value, "yang-version"); |
| goto error; |
| } |
| module->version = 1; |
| lyxml_free_elem(ctx, node); |
| } |
| } |
| |
| /* check for mandatory statements */ |
| if (!module->ns) { |
| ly_verr(LY_VERR_MISS_STMT2, "namespace", "module"); |
| goto error; |
| } |
| if (!module->prefix) { |
| ly_verr(LY_VERR_MISS_STMT2, "prefix", "module"); |
| goto error; |
| } |
| |
| |
| /* 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")) { |
| r = fill_yin_typedef(module, NULL, node, &module->tpdf[c_tpdf]); |
| c_tpdf++; |
| |
| if (r) { |
| module->tpdf_size = c_tpdf; |
| goto error; |
| } |
| } |
| |
| lyxml_free_elem(ctx, node); |
| } |
| |
| /* last part - process data nodes */ |
| LY_TREE_FOR_SAFE(root.child, next, node) { |
| |
| if (!strcmp(node->name, "container")) { |
| mnode = read_yin_container(module, NULL, node); |
| } else if (!strcmp(node->name, "leaf-list")) { |
| mnode = read_yin_leaflist(module, NULL, node); |
| } else if (!strcmp(node->name, "leaf")) { |
| mnode = read_yin_leaf(module, NULL, node); |
| } else if (!strcmp(node->name, "list")) { |
| mnode = read_yin_list(module, NULL, node); |
| } else if (!strcmp(node->name, "choice")) { |
| mnode = read_yin_choice(module, NULL, node); |
| } else if (!strcmp(node->name, "grouping")) { |
| mnode = read_yin_grouping(module, NULL, node); |
| } else { |
| /* TODO error */ |
| continue; |
| } |
| lyxml_free_elem(ctx, node); |
| |
| if (!mnode) { |
| goto error; |
| } |
| |
| /* include data element */ |
| if (module->data) { |
| module->data->prev->next = mnode; |
| mnode->prev = module->data->prev; |
| module->data->prev = mnode; |
| } else { |
| module->data = mnode; |
| } |
| } |
| |
| /* 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 */ |
| while (root.child) { |
| lyxml_free_elem(module->ctx, root.child); |
| } |
| lyxml_free_elem(ctx, yin); |
| ly_model_free(module); |
| |
| return NULL; |
| } |