yang parser NEW inital yang parser version
Temporarily with main() function.
diff --git a/src/parser_yang.c b/src/parser_yang.c
new file mode 100644
index 0000000..a438551
--- /dev/null
+++ b/src/parser_yang.c
@@ -0,0 +1,4734 @@
+/**
+ * @file parser_yang.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG parser
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <assert.h>
+
+#include "common.h"
+#include "context.h"
+#include "libyang.h"
+
+#define YANG_ERR_GOTO(ERR, GO) if (ERR) goto GO;
+
+#define YANG_ERR_RET(ERR) if (ERR) return ERR;
+
+#define YANG_READ_SUBSTMT_FOR(CTX, DATA, KW, WORD, WORD_LEN, ERR) \
+ ERR = get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN); \
+ YANG_ERR_RET(ERR); \
+ \
+ if (KW == YANG_SEMICOLON) { \
+ return ERR; \
+ } \
+ if (KW != YANG_LEFT_BRACE) { \
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", expected \";\" or \"{\".", kw2str(KW)); \
+ return LY_EVALID; \
+ } \
+ for (ERR = get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN); \
+ !ERR && (KW != YANG_RIGHT_BRACE); \
+ ERR = get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN))
+
+enum yang_module_stmt {
+ Y_MOD_MODULE_HEADER,
+ Y_MOD_LINKAGE,
+ Y_MOD_META,
+ Y_MOD_REVISION,
+ Y_MOD_BODY
+};
+
+enum yang_arg {
+ Y_IDENTIF_ARG,
+ Y_PREF_IDENTIF_ARG,
+ Y_STR_ARG,
+ Y_MAYBE_STR_ARG
+};
+
+enum yang_keyword {
+ YANG_NONE = 0,
+ YANG_ACTION,
+ YANG_ANYDATA,
+ YANG_ANYXML,
+ YANG_ARGUMENT,
+ YANG_AUGMENT,
+ YANG_BASE,
+ YANG_BELONGS_TO,
+ YANG_BIT,
+ YANG_CASE,
+ YANG_CHOICE,
+ YANG_CONFIG,
+ YANG_CONTACT,
+ YANG_CONTAINER,
+ YANG_DEFAULT,
+ YANG_DESCRIPTION,
+ YANG_DEVIATE,
+ YANG_DEVIATION,
+ YANG_ENUM,
+ YANG_ERROR_APP_TAG,
+ YANG_ERROR_MESSAGE,
+ YANG_EXTENSION,
+ YANG_FEATURE,
+ YANG_FRACTION_DIGITS,
+ YANG_GROUPING,
+ YANG_IDENTITY,
+ YANG_IF_FEATURE,
+ YANG_IMPORT,
+ YANG_INCLUDE,
+ YANG_INPUT,
+ YANG_KEY,
+ YANG_LEAF,
+ YANG_LEAF_LIST,
+ YANG_LENGTH,
+ YANG_LIST,
+ YANG_MANDATORY,
+ YANG_MAX_ELEMENTS,
+ YANG_MIN_ELEMENTS,
+ YANG_MODIFIER,
+ YANG_MODULE,
+ YANG_MUST,
+ YANG_NAMESPACE,
+ YANG_NOTIFICATION,
+ YANG_ORDERED_BY,
+ YANG_ORGANIZATION,
+ YANG_OUTPUT,
+ YANG_PATH,
+ YANG_PATTERN,
+ YANG_POSITION,
+ YANG_PREFIX,
+ YANG_PRESENCE,
+ YANG_RANGE,
+ YANG_REFERENCE,
+ YANG_REFINE,
+ YANG_REQUIRE_INSTANCE,
+ YANG_REVISION,
+ YANG_REVISION_DATE,
+ YANG_RPC,
+ YANG_STATUS,
+ YANG_SUBMODULE,
+ YANG_TYPE,
+ YANG_TYPEDEF,
+ YANG_UNIQUE,
+ YANG_UNITS,
+ YANG_USES,
+ YANG_VALUE,
+ YANG_WHEN,
+ YANG_YANG_VERSION,
+ YANG_YIN_ELEMENT,
+
+ YANG_SEMICOLON,
+ YANG_LEFT_BRACE,
+ YANG_RIGHT_BRACE,
+ YANG_CUSTOM
+};
+
+static LY_ERR parse_container(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings);
+static LY_ERR parse_uses(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings);
+static LY_ERR parse_choice(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings);
+static LY_ERR parse_case(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings);
+static LY_ERR parse_list(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings);
+static LY_ERR parse_grouping(struct ly_ctx *ctx, const char **data, struct lysp_grp **groupings);
+
+static const char *
+kw2str(enum yang_keyword kw)
+{
+ switch (kw) {
+ case YANG_ACTION:
+ return "action";
+ case YANG_ANYDATA:
+ return "anydata";
+ case YANG_ANYXML:
+ return "anyxml";
+ case YANG_ARGUMENT:
+ return "argument";
+ case YANG_AUGMENT:
+ return "augment";
+ case YANG_BASE:
+ return "base";
+ case YANG_BELONGS_TO:
+ return "belongs-to";
+ case YANG_BIT:
+ return "bit";
+ case YANG_CASE:
+ return "case";
+ case YANG_CHOICE:
+ return "choice";
+ case YANG_CONFIG:
+ return "config";
+ case YANG_CONTACT:
+ return "contact";
+ case YANG_CONTAINER:
+ return "container";
+ case YANG_CUSTOM:
+ return "<extension-instance>";
+ case YANG_DEFAULT:
+ return "default";
+ case YANG_DESCRIPTION:
+ return "description";
+ case YANG_DEVIATE:
+ return "deviate";
+ case YANG_DEVIATION:
+ return "deviation";
+ case YANG_ENUM:
+ return "enum";
+ case YANG_ERROR_APP_TAG:
+ return "error-app-tag";
+ case YANG_ERROR_MESSAGE:
+ return "error-message";
+ case YANG_EXTENSION:
+ return "extension";
+ case YANG_FEATURE:
+ return "feature";
+ case YANG_FRACTION_DIGITS:
+ return "fraction-digits";
+ case YANG_GROUPING:
+ return "grouping";
+ case YANG_IDENTITY:
+ return "identitiy";
+ case YANG_IF_FEATURE:
+ return "if-feature";
+ case YANG_IMPORT:
+ return "import";
+ case YANG_INCLUDE:
+ return "include";
+ case YANG_INPUT:
+ return "input";
+ case YANG_KEY:
+ return "key";
+ case YANG_LEAF:
+ return "leaf";
+ case YANG_LEAF_LIST:
+ return "leaf-list";
+ case YANG_LENGTH:
+ return "length";
+ case YANG_LIST:
+ return "list";
+ case YANG_MANDATORY:
+ return "mandatory";
+ case YANG_MAX_ELEMENTS:
+ return "max-elements";
+ case YANG_MIN_ELEMENTS:
+ return "min-elements";
+ case YANG_MODIFIER:
+ return "modifier";
+ case YANG_MODULE:
+ return "module";
+ case YANG_MUST:
+ return "must";
+ case YANG_NAMESPACE:
+ return "namespace";
+ case YANG_NOTIFICATION:
+ return "notification";
+ case YANG_ORDERED_BY:
+ return "ordered-by";
+ case YANG_ORGANIZATION:
+ return "organization";
+ case YANG_OUTPUT:
+ return "output";
+ case YANG_PATH:
+ return "path";
+ case YANG_PATTERN:
+ return "pattern";
+ case YANG_POSITION:
+ return "position";
+ case YANG_PREFIX:
+ return "prefix";
+ case YANG_PRESENCE:
+ return "presence";
+ case YANG_RANGE:
+ return "range";
+ case YANG_REFERENCE:
+ return "reference";
+ case YANG_REFINE:
+ return "refine";
+ case YANG_REQUIRE_INSTANCE:
+ return "require-instance";
+ case YANG_REVISION:
+ return "revision";
+ case YANG_REVISION_DATE:
+ return "revision-date";
+ case YANG_RPC:
+ return "rpc";
+ case YANG_STATUS:
+ return "status";
+ case YANG_SUBMODULE:
+ return "submodule";
+ case YANG_TYPE:
+ return "type";
+ case YANG_TYPEDEF:
+ return "typedef";
+ case YANG_UNIQUE:
+ return "unique";
+ case YANG_UNITS:
+ return "units";
+ case YANG_USES:
+ return "uses";
+ case YANG_VALUE:
+ return "value";
+ case YANG_WHEN:
+ return "when";
+ case YANG_YANG_VERSION:
+ return "yang-version";
+ case YANG_YIN_ELEMENT:
+ return "yin-element";
+ case YANG_SEMICOLON:
+ return ";";
+ case YANG_LEFT_BRACE:
+ return "{";
+ case YANG_RIGHT_BRACE:
+ return "}";
+ default:
+ return "";
+ }
+}
+
+static const char *
+substmt2str(LYEXT_SUBSTMT substmt)
+{
+ switch (substmt) {
+ case LYEXT_SUBSTMT_ARGUMENT:
+ return "argument";
+ case LYEXT_SUBSTMT_BASE:
+ return "base";
+ case LYEXT_SUBSTMT_BELONGSTO:
+ return "belongs-to";
+ case LYEXT_SUBSTMT_CONTACT:
+ return "contact";
+ case LYEXT_SUBSTMT_DEFAULT:
+ return "default";
+ case LYEXT_SUBSTMT_DESCRIPTION:
+ return "description";
+ case LYEXT_SUBSTMT_ERRTAG:
+ return "error-app-tag";
+ case LYEXT_SUBSTMT_ERRMSG:
+ return "error-message";
+ case LYEXT_SUBSTMT_KEY:
+ return "key";
+ case LYEXT_SUBSTMT_NAMESPACE:
+ return "namespace";
+ case LYEXT_SUBSTMT_ORGANIZATION:
+ return "organization";
+ case LYEXT_SUBSTMT_PATH:
+ return "path";
+ case LYEXT_SUBSTMT_PREFIX:
+ return "prefix";
+ case LYEXT_SUBSTMT_PRESENCE:
+ return "presence";
+ case LYEXT_SUBSTMT_REFERENCE:
+ return "reference";
+ case LYEXT_SUBSTMT_REVISIONDATE:
+ return "revision-date";
+ case LYEXT_SUBSTMT_UNITS:
+ return "units";
+ case LYEXT_SUBSTMT_VALUE:
+ return "value";
+ case LYEXT_SUBSTMT_VERSION:
+ return "yang-version";
+ case LYEXT_SUBSTMT_MODIFIER:
+ return "modifier";
+ case LYEXT_SUBSTMT_REQINSTANCE:
+ return "require-instance";
+ case LYEXT_SUBSTMT_YINELEM:
+ return "yin-element";
+ case LYEXT_SUBSTMT_CONFIG:
+ return "config";
+ case LYEXT_SUBSTMT_MANDATORY:
+ return "mandatory";
+ case LYEXT_SUBSTMT_ORDEREDBY:
+ return "ordered-by";
+ case LYEXT_SUBSTMT_STATUS:
+ return "status";
+ case LYEXT_SUBSTMT_FRACDIGITS:
+ return "fraction-digits";
+ case LYEXT_SUBSTMT_MAX:
+ return "max-elements";
+ case LYEXT_SUBSTMT_MIN:
+ return "min-elements";
+ case LYEXT_SUBSTMT_POSITION:
+ return "position";
+ case LYEXT_SUBSTMT_UNIQUE:
+ return "unique";
+ case LYEXT_SUBSTMT_IFFEATURE:
+ return "if-feature";
+ default:
+ return "";
+ }
+}
+
+static const char *
+nodetype2str(uint16_t nodetype)
+{
+ switch (nodetype) {
+ case LYS_CONTAINER:
+ return "container";
+ case LYS_CHOICE:
+ return "choice";
+ case LYS_LEAF:
+ return "leaf";
+ case LYS_LEAFLIST:
+ return "leaf-list";
+ case LYS_LIST:
+ return "list";
+ case LYS_ANYXML:
+ return "anyxml";
+ case LYS_CASE:
+ return "case";
+ case LYS_USES:
+ return "uses";
+ case LYS_ANYDATA:
+ return "anydata";
+ default:
+ return "";
+ }
+}
+
+static const char *
+devmod2str(uint8_t devmod)
+{
+ switch (devmod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ return "not-supported";
+ case LYS_DEV_ADD:
+ return "add";
+ case LYS_DEV_DELETE:
+ return "delete";
+ case LYS_DEV_REPLACE:
+ return "replace";
+ default:
+ return "";
+ }
+}
+
+static LY_ERR
+buf_add_char(struct ly_ctx *ctx, char c, char **buf, int *buf_len, int buf_used)
+{
+ if (*buf_len == buf_used) {
+ *buf_len += 16;
+ *buf = ly_realloc(*buf, *buf_len);
+ LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
+ }
+
+ (*buf)[buf_used] = c;
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+buf_store_char(struct ly_ctx *ctx, const char *c, char **word_p, int *word_len, char **word_b, int *buf_len, int need_buf)
+{
+ if (word_b && *word_b) {
+ /* add another character into buffer */
+ if (buf_add_char(ctx, *c, word_b, buf_len, *word_len)) {
+ return LY_EMEM;
+ }
+
+ /* in case of realloc */
+ *word_p = *word_b;
+ } else if (need_buf) {
+ /* first time we need a buffer, copy everything read up to now */
+ if (*word_len) {
+ *word_b = malloc(*word_len);
+ LY_CHECK_ERR_RET(!*word_b, LOGMEM(ctx), LY_EMEM);
+ *buf_len = *word_len;
+ memcpy(*word_b, *word_p, *word_len);
+ }
+
+ /* add this new character into buffer */
+ if (buf_add_char(ctx, *c, word_b, buf_len, *word_len)) {
+ return LY_EMEM;
+ }
+
+ /* in case of realloc */
+ *word_p = *word_b;
+ } else {
+ /* just remember the first character pointer */
+ if (!*word_p) {
+ *word_p = (char *)c;
+ }
+ }
+
+ ++(*word_len);
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+check_char(struct ly_ctx *ctx, char c, enum yang_arg arg, int first, int *prefix)
+{
+ if ((arg == Y_STR_ARG) || (arg == Y_MAYBE_STR_ARG)) {
+ if ((c < ' ') && (c != '\t') && (c != '\n')) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHAR, c);
+ return LY_EVALID;
+ }
+ } else if ((arg == Y_IDENTIF_ARG) || (arg == Y_PREF_IDENTIF_ARG)) {
+ if (c < ' ') {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHAR, c);
+ return LY_EVALID;
+ }
+
+ if (first || (*prefix == 2)) {
+ /* either first character of the whole identifier or of the name after prefix */
+ if (!isalpha(c) && (c != '_')) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG, "Invalid identifier first character '%c'.", c);
+ return LY_EVALID;
+ }
+ if (*prefix == 2) {
+ *prefix = 1;
+ }
+ } else {
+ /* other characters */
+ if (!isalnum(c) && (c != '_') && (c != '-') && (c != '.')) {
+ if ((arg == Y_PREF_IDENTIF_ARG) && !*prefix && (c == ':')) {
+ /* this is fine */
+ *prefix = 2;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG, "Invalid identifier character '%c'.", c);
+ return LY_EVALID;
+ }
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+skip_comment(struct ly_ctx *ctx, const char **data, int comment)
+{
+ /* comment: 0 - comment ended, 1 - in line comment, 2 - in block comment, 3 - in block comment with last read character '*' */
+
+ while (**data && comment) {
+ switch (comment) {
+ case 1:
+ if (**data == '\n') {
+ comment = 0;
+ }
+ break;
+ case 2:
+ if (**data == '*') {
+ comment = 3;
+ }
+ break;
+ case 3:
+ if (**data == '/') {
+ comment = 0;
+ } else {
+ comment = 2;
+ }
+ break;
+ default:
+ LOGINT_RET(ctx);
+ }
+
+ ++(*data);
+ }
+
+ if (!**data && (comment > 1)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX, "Unexpected end-of-file, non-terminated comment.");
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+read_qstring(struct ly_ctx *ctx, const char **data, enum yang_arg arg, char **word_p, char **word_b, int *word_len,
+ int *buf_len, int indent)
+{
+ /* string: 0 - string ended, 1 - string with ', 2 - string with ", 3 - string with " with last character \,
+ * 4 - string finished, now skipping whitespaces looking for +,
+ * 5 - string continues after +, skipping whitespaces */
+ int string, str_nl_indent = 0, need_buf = 0, prefix = 0;
+ const char *c;
+
+ if (**data == '\"') {
+ /* indent of the " itself */
+ ++indent;
+ string = 2;
+ } else {
+ assert(**data == '\'');
+ string = 1;
+ }
+ ++(*data);
+
+ while (**data && string) {
+ switch (string) {
+ case 1:
+ switch (**data) {
+ case '\'':
+ /* string may be finished, but check for + */
+ string = 4;
+ break;
+ default:
+ /* check and store character */
+ if (check_char(ctx, **data, arg, !*word_len, &prefix)) {
+ return LY_EVALID;
+ }
+ if (buf_store_char(ctx, *data, word_p, word_len, word_b, buf_len, need_buf)) {
+ return LY_EMEM;
+ }
+ break;
+ }
+ break;
+ case 2:
+ switch (**data) {
+ case '\"':
+ /* string may be finished, but check for + */
+ string = 4;
+ break;
+ case '\\':
+ /* special character following */
+ string = 3;
+ break;
+ case ' ':
+ if (str_nl_indent) {
+ --str_nl_indent;
+ } else {
+ /* check and store character */
+ if (check_char(ctx, **data, arg, !*word_len, &prefix)) {
+ return LY_EVALID;
+ }
+ if (buf_store_char(ctx, *data, word_p, word_len, word_b, buf_len, need_buf)) {
+ return LY_EMEM;
+ }
+ }
+ break;
+ case '\t':
+ if (str_nl_indent) {
+ if (str_nl_indent < 9) {
+ str_nl_indent = 0;
+ } else {
+ str_nl_indent -= 8;
+ }
+ } else {
+ /* check and store character */
+ if (check_char(ctx, **data, arg, !*word_len, &prefix)) {
+ return LY_EVALID;
+ }
+ if (buf_store_char(ctx, *data, word_p, word_len, word_b, buf_len, need_buf)) {
+ return LY_EMEM;
+ }
+ }
+ break;
+ case '\n':
+ str_nl_indent = indent;
+ if (indent) {
+ /* we will be removing the indents so we need our own buffer */
+ need_buf = 1;
+ }
+
+ /* check and store character */
+ if (check_char(ctx, **data, arg, !*word_len, &prefix)) {
+ return LY_EVALID;
+ }
+ if (buf_store_char(ctx, *data, word_p, word_len, word_b, buf_len, need_buf)) {
+ return LY_EMEM;
+ }
+ break;
+ default:
+ /* first non-whitespace character, clear current indent */
+ str_nl_indent = 0;
+
+ /* check and store character */
+ if (check_char(ctx, **data, arg, !*word_len, &prefix)) {
+ return LY_EVALID;
+ }
+ if (buf_store_char(ctx, *data, word_p, word_len, word_b, buf_len, need_buf)) {
+ return LY_EMEM;
+ }
+ break;
+ }
+ break;
+ case 3:
+ /* string encoded characters */
+ switch (**data) {
+ case 'n':
+ c = "\n";
+ break;
+ case 't':
+ c = "\t";
+ break;
+ case '\"':
+ c = *data;
+ break;
+ case '\\':
+ c = *data;
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Double-quoted string unknown special character '\\%c'.\n", **data);
+ return LY_EVALID;
+ }
+
+ /* check and store character */
+ if (check_char(ctx, *c, arg, !*word_len, &prefix)) {
+ return LY_EVALID;
+ }
+ if (buf_store_char(ctx, c, word_p, word_len, word_b, buf_len, need_buf)) {
+ return LY_EMEM;
+ }
+
+ string = 2;
+ break;
+ case 4:
+ switch (**data) {
+ case '+':
+ /* string continues */
+ string = 5;
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ /* just skip */
+ break;
+ default:
+ /* string is finished */
+ goto string_end;
+ }
+ break;
+ case 5:
+ switch (**data) {
+ case ' ':
+ case '\t':
+ case '\n':
+ /* skip */
+ break;
+ case '\'':
+ string = 1;
+ break;
+ case '\"':
+ string = 2;
+ break;
+ default:
+ /* it must be quoted again */
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Both string parts divided by '+' must be quoted.\n");
+ return LY_EVALID;
+ }
+ break;
+ default:
+ return LY_EINT;
+ }
+
+ ++(*data);
+ }
+
+string_end:
+ return LY_SUCCESS;
+}
+
+/* read another word - character sequence without whitespaces (logs directly)
+ * - there can be whitespaces if they are a part of string
+ * - strings are always returned separately even if not separated by whitespaces
+ * - strings are returned without ' or "
+ * - strings divided by + are returned concatenated
+ * - comments are skipped
+ */
+static LY_ERR
+get_string(struct ly_ctx *ctx, const char **data, enum yang_arg arg, char **word_p, char **word_b, int *word_len)
+{
+ int buf_len = 0, slash = 0, indent = 0, prefix = 0;
+ LY_ERR ret;
+
+ /* word buffer - dynamically allocated */
+ *word_b = NULL;
+
+ /* word pointer - just a pointer to data */
+ *word_p = NULL;
+
+ *word_len = 0;
+ while (**data) {
+ if (slash) {
+ if (**data == '/') {
+ /* one-line comment */
+ ret = skip_comment(ctx, data, 1);
+ if (ret) {
+ return ret;
+ }
+ } else if (**data == '*') {
+ /* block comment */
+ ret = skip_comment(ctx, data, 2);
+ if (ret) {
+ return ret;
+ }
+ } else {
+ /* not a comment after all */
+ ret = buf_store_char(ctx, (*data) - 1, word_p, word_len, word_b, &buf_len, 0);
+ if (ret) {
+ return ret;
+ }
+ }
+ slash = 0;
+ }
+
+ switch (**data) {
+ case '\'':
+ case '\"':
+ if (*word_len) {
+ /* we want strings always in a separate word, leave it */
+ goto str_end;
+ }
+ ret = read_qstring(ctx, data, arg, word_p, word_b, word_len, &buf_len, indent);
+ if (ret) {
+ return ret;
+ }
+ goto str_end;
+ case '/':
+ slash = 1;
+ break;
+ case ' ':
+ if (*word_len) {
+ /* word is finished */
+ goto str_end;
+ }
+ /* increase indent */
+ ++indent;
+ break;
+ case '\t':
+ if (*word_len) {
+ /* word is finished */
+ goto str_end;
+ }
+ /* tabs count for 8 spaces */
+ indent += 8;
+ break;
+ case '\n':
+ if (*word_len) {
+ /* word is finished */
+ goto str_end;
+ }
+ /* reset indent */
+ indent = 0;
+ break;
+ case ';':
+ case '{':
+ if (*word_len || (arg == Y_MAYBE_STR_ARG)) {
+ /* word is finished */
+ goto str_end;
+ }
+
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INSTREXP, 1, *data, "an argument");
+ return LY_EVALID;
+ default:
+ if (check_char(ctx, **data, arg, !*word_len, &prefix)) {
+ return LY_EVALID;
+ }
+
+ if (buf_store_char(ctx, *data, word_p, word_len, word_b, &buf_len, 0)) {
+ return LY_EMEM;
+ }
+ break;
+ }
+
+ ++(*data);
+ }
+
+str_end:
+ /* ending '\0' for buf */
+ if (*word_b) {
+ if (buf_add_char(ctx, '\0', word_b, &buf_len, *word_len)) {
+ return LY_EMEM;
+ }
+ *word_p = *word_b;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+get_keyword(struct ly_ctx *ctx, const char **data, enum yang_keyword *kw, char **word_p, int *word_len)
+{
+ LY_ERR ret;
+ int prefix;
+ const char *word_start;
+ /* slash: 0 - nothing, 1 - last character was '/' */
+ int slash = 0;
+
+ if (word_p) {
+ *word_p = NULL;
+ *word_len = 0;
+ }
+
+ /* first skip "optsep", comments */
+ while (**data) {
+ if (slash) {
+ if (**data == '/') {
+ /* one-line comment */
+ ret = skip_comment(ctx, data, 1);
+ if (ret) {
+ return ret;
+ }
+ } else if (**data == '*') {
+ /* block comment */
+ ret = skip_comment(ctx, data, 2);
+ if (ret) {
+ return ret;
+ }
+ } else {
+ /* not a comment after all */
+ goto keyword_start;
+ }
+ slash = 0;
+ }
+
+ switch (**data) {
+ case '/':
+ slash = 1;
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ /* skip whitespaces (optsep) */
+ break;
+ default:
+ /* either a keyword start or an invalid character */
+ goto keyword_start;
+ }
+
+ ++(*data);
+ }
+
+keyword_start:
+ word_start = *data;
+ *kw = YANG_NONE;
+
+ /* read the keyword itself */
+ switch (**data) {
+ case 'a':
+ ++(*data);
+ if (!strncmp(*data, "rgument", 7)) {
+ *data += 7;
+ *kw = YANG_ARGUMENT;
+ } else if (!strncmp(*data, "ugment", 6)) {
+ *data += 6;
+ *kw = YANG_AUGMENT;
+ } else if (!strncmp(*data, "ction", 5)) {
+ *data += 5;
+ *kw = YANG_ACTION;
+ } else if (!strncmp(*data, "ny", 2)) {
+ *data += 2;
+ if (!strncmp(*data, "data", 4)) {
+ *data += 4;
+ *kw = YANG_ANYDATA;
+ } else if (!strncmp(*data, "xml", 3)) {
+ *data += 3;
+ *kw = YANG_ANYXML;
+ }
+ }
+ break;
+ case 'b':
+ ++(*data);
+ if (!strncmp(*data, "ase", 3)) {
+ *data += 3;
+ *kw = YANG_BASE;
+ } else if (!strncmp(*data, "elongs-to", 9)) {
+ *data += 9;
+ *kw = YANG_BELONGS_TO;
+ } else if (!strncmp(*data, "it", 2)) {
+ *data += 2;
+ *kw = YANG_BIT;
+ }
+ break;
+ case 'c':
+ ++(*data);
+ if (!strncmp(*data, "ase", 3)) {
+ *data += 3;
+ *kw = YANG_CASE;
+ } else if (!strncmp(*data, "hoice", 5)) {
+ *data += 5;
+ *kw = YANG_CHOICE;
+ } else if (!strncmp(*data, "on", 2)) {
+ *data += 2;
+ if (!strncmp(*data, "fig", 3)) {
+ *data += 3;
+ *kw = YANG_CONFIG;
+ } else if (!strncmp(*data, "ta", 2)) {
+ *data += 2;
+ if (!strncmp(*data, "ct", 2)) {
+ *data += 2;
+ *kw = YANG_CONTACT;
+ } else if (!strncmp(*data, "iner", 4)) {
+ *data += 4;
+ *kw = YANG_CONTAINER;
+ }
+ }
+ }
+ break;
+ case 'd':
+ ++(*data);
+ if (**data == 'e') {
+ ++(*data);
+ if (!strncmp(*data, "fault", 5)) {
+ *data += 5;
+ *kw = YANG_DEFAULT;
+ } else if (!strncmp(*data, "scription", 9)) {
+ *data += 9;
+ *kw = YANG_DESCRIPTION;
+ } else if (!strncmp(*data, "viat", 4)) {
+ *data += 4;
+ if (**data == 'e') {
+ ++(*data);
+ *kw = YANG_DEVIATE;
+ } else if (!strncmp(*data, "ion", 3)) {
+ *data += 3;
+ *kw = YANG_DEVIATION;
+ }
+ }
+ }
+ break;
+ case 'e':
+ ++(*data);
+ if (!strncmp(*data, "num", 3)) {
+ *data += 3;
+ *kw = YANG_ENUM;
+ } else if (!strncmp(*data, "rror-", 5)) {
+ *data += 5;
+ if (!strncmp(*data, "app-tag", 7)) {
+ *data += 7;
+ *kw = YANG_ERROR_APP_TAG;
+ } else if (!strncmp(*data, "message", 7)) {
+ *data += 7;
+ *kw = YANG_ERROR_MESSAGE;
+ }
+ } else if (!strncmp(*data, "xtension", 8)) {
+ *data += 8;
+ *kw = YANG_EXTENSION;
+ }
+ break;
+ case 'f':
+ ++(*data);
+ if (!strncmp(*data, "eature", 6)) {
+ *data += 6;
+ *kw = YANG_FEATURE;
+ } else if (!strncmp(*data, "raction-digits", 14)) {
+ *data += 14;
+ *kw = YANG_FRACTION_DIGITS;
+ }
+ break;
+ case 'g':
+ ++(*data);
+ if (!strncmp(*data, "rouping", 7)) {
+ *data += 7;
+ *kw = YANG_GROUPING;
+ }
+ break;
+ case 'i':
+ ++(*data);
+ if (!strncmp(*data, "dentity", 7)) {
+ *data += 7;
+ *kw = YANG_IDENTITY;
+ } else if (!strncmp(*data, "f-feature", 9)) {
+ *data += 9;
+ *kw = YANG_IF_FEATURE;
+ } else if (!strncmp(*data, "mport", 5)) {
+ *data += 5;
+ *kw = YANG_IMPORT;
+ } else if (**data == 'n') {
+ ++(*data);
+ if (!strncmp(*data, "clude", 5)) {
+ *data += 5;
+ *kw = YANG_INCLUDE;
+ } else if (!strncmp(*data, "put", 3)) {
+ *data += 3;
+ *kw = YANG_INPUT;
+ }
+ }
+ break;
+ case 'k':
+ ++(*data);
+ if (!strncmp(*data, "ey", 2)) {
+ *data += 2;
+ *kw = YANG_KEY;
+ }
+ break;
+ case 'l':
+ ++(*data);
+ if (**data == 'e') {
+ ++(*data);
+ if (!strncmp(*data, "af", 2)) {
+ *data += 2;
+ if (**data != '-') {
+ *kw = YANG_LEAF;
+ } else if (!strncmp(*data, "-list", 5)) {
+ *data += 5;
+ *kw = YANG_LEAF_LIST;
+ }
+ } else if (!strncmp(*data, "ngth", 4)) {
+ *data += 4;
+ *kw = YANG_LENGTH;
+ }
+ } else if (!strncmp(*data, "ist", 3)) {
+ *data += 3;
+ *kw = YANG_LIST;
+ }
+ break;
+ case 'm':
+ ++(*data);
+ if (**data == 'a') {
+ ++(*data);
+ if (!strncmp(*data, "ndatory", 7)) {
+ *data += 7;
+ *kw = YANG_MANDATORY;
+ } else if (!strncmp(*data, "x-elements", 10)) {
+ *data += 10;
+ *kw = YANG_MAX_ELEMENTS;
+ }
+ } else if (!strncmp(*data, "in-elements", 11)) {
+ *data += 11;
+ *kw = YANG_MIN_ELEMENTS;
+ } else if (!strncmp(*data, "ust", 3)) {
+ *data += 3;
+ *kw = YANG_MUST;
+ } else if (!strncmp(*data, "od", 2)) {
+ *data += 2;
+ if (!strncmp(*data, "ule", 3)) {
+ *data += 3;
+ *kw = YANG_MODULE;
+ } else if (!strncmp(*data, "ifier", 5)) {
+ *data += 3;
+ *kw = YANG_MODIFIER;
+ }
+ }
+ break;
+ case 'n':
+ ++(*data);
+ if (!strncmp(*data, "amespace", 8)) {
+ *data += 8;
+ *kw = YANG_NAMESPACE;
+ } else if (!strncmp(*data, "otification", 11)) {
+ *data += 11;
+ *kw = YANG_NOTIFICATION;
+ }
+ break;
+ case 'o':
+ ++(*data);
+ if (**data == 'r') {
+ ++(*data);
+ if (!strncmp(*data, "dered-by", 8)) {
+ *data += 8;
+ *kw = YANG_ORDERED_BY;
+ } else if (!strncmp(*data, "ganization", 10)) {
+ *data += 10;
+ *kw = YANG_ORGANIZATION;
+ }
+ } else if (!strncmp(*data, "utput", 5)) {
+ *data += 5;
+ *kw = YANG_OUTPUT;
+ }
+ break;
+ case 'p':
+ ++(*data);
+ if (!strncmp(*data, "at", 2)) {
+ *data += 2;
+ if (**data == 'h') {
+ ++(*data);
+ *kw = YANG_PATH;
+ } else if (!strncmp(*data, "tern", 4)) {
+ *data += 4;
+ *kw = YANG_PATTERN;
+ }
+ } else if (!strncmp(*data, "osition", 7)) {
+ *data += 7;
+ *kw = YANG_POSITION;
+ } else if (!strncmp(*data, "re", 2)) {
+ *data += 2;
+ if (!strncmp(*data, "fix", 3)) {
+ *data += 3;
+ *kw = YANG_PREFIX;
+ } else if (!strncmp(*data, "sence", 5)) {
+ *data += 5;
+ *kw = YANG_PRESENCE;
+ }
+ }
+ break;
+ case 'r':
+ ++(*data);
+ if (!strncmp(*data, "ange", 4)) {
+ *data += 4;
+ *kw = YANG_RANGE;
+ } else if (**data == 'e') {
+ ++(*data);
+ if (**data == 'f') {
+ ++(*data);
+ if (!strncmp(*data, "erence", 6)) {
+ *data += 6;
+ *kw = YANG_REFERENCE;
+ } else if (!strncmp(*data, "ine", 3)) {
+ *data += 3;
+ *kw = YANG_REFINE;
+ }
+ } else if (!strncmp(*data, "quire-instance", 14)) {
+ *data += 14;
+ *kw = YANG_REQUIRE_INSTANCE;
+ } else if (!strncmp(*data, "vision", 6)) {
+ *data += 6;
+ if (**data != '-') {
+ *kw = YANG_REVISION;
+ } else if (!strncmp(*data, "-date", 5)) {
+ *data += 5;
+ *kw = YANG_REVISION_DATE;
+ }
+ }
+ } else if (!strncmp(*data, "pc", 2)) {
+ *data += 2;
+ *kw = YANG_RPC;
+ }
+ break;
+ case 's':
+ ++(*data);
+ if (!strncmp(*data, "tatus", 5)) {
+ *data += 5;
+ *kw = YANG_STATUS;
+ } else if (!strncmp(*data, "ubmodule", 8)) {
+ *data += 8;
+ *kw = YANG_SUBMODULE;
+ }
+ break;
+ case 't':
+ ++(*data);
+ if (!strncmp(*data, "ype", 3)) {
+ *data += 3;
+ if (**data != 'd') {
+ *kw = YANG_TYPE;
+ } else if (!strncmp(*data, "def", 3)) {
+ *data += 3;
+ *kw = YANG_TYPEDEF;
+ }
+ }
+ break;
+ case 'u':
+ ++(*data);
+ if (!strncmp(*data, "ni", 2)) {
+ *data += 2;
+ if (!strncmp(*data, "que", 3)) {
+ *data += 3;
+ *kw = YANG_UNIQUE;
+ } else if (!strncmp(*data, "ts", 2)) {
+ *data += 2;
+ *kw = YANG_UNITS;
+ }
+ } else if (!strncmp(*data, "ses", 3)) {
+ *data += 3;
+ *kw = YANG_USES;
+ }
+ break;
+ case 'v':
+ ++(*data);
+ if (!strncmp(*data, "alue", 4)) {
+ *data += 4;
+ *kw = YANG_VALUE;
+ }
+ break;
+ case 'w':
+ ++(*data);
+ if (!strncmp(*data, "hen", 3)) {
+ *data += 3;
+ *kw = YANG_WHEN;
+ }
+ break;
+ case 'y':
+ ++(*data);
+ if (!strncmp(*data, "ang-version", 11)) {
+ *data += 11;
+ *kw = YANG_YANG_VERSION;
+ } else if (!strncmp(*data, "in-element", 10)) {
+ *data += 10;
+ *kw = YANG_YIN_ELEMENT;
+ }
+ break;
+ case ';':
+ ++(*data);
+ *kw = YANG_SEMICOLON;
+ break;
+ case '{':
+ ++(*data);
+ *kw = YANG_LEFT_BRACE;
+ break;
+ case '}':
+ ++(*data);
+ *kw = YANG_RIGHT_BRACE;
+ break;
+ default:
+ break;
+ }
+
+ if (*kw != YANG_NONE) {
+ /* make sure we have the whole keyword */
+ switch (**data) {
+ case ' ':
+ case '\t':
+ case '\n':
+ /* mandatory "sep" */
+ break;
+ default:
+ ++(*data);
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INSTREXP, (int)(*data - word_start), word_start,
+ "a keyword followed by a separator");
+ return LY_EVALID;
+ }
+ } else {
+ /* still can be an extension */
+ prefix = 0;
+ while (**data && (**data != ' ') && (**data != '\t') && (**data != '\n') && (**data != '{') && (**data != ';')) {
+ if (check_char(ctx, **data, Y_PREF_IDENTIF_ARG, *data == word_start ? 1 : 0, &prefix)) {
+ return LY_EVALID;
+ }
+ ++(*data);
+ }
+ if (!**data) {
+ /* LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_EOF); */
+ return LY_EVALID;
+ }
+
+ /* prefix is mandatory for extension instances */
+ if (prefix != 1) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INSTREXP, (int)(*data - word_start), word_start, "a keyword");
+ return LY_EVALID;
+ }
+
+ *kw = YANG_CUSTOM;
+ }
+
+ if (word_p) {
+ *word_p = (char *)word_start;
+ *word_len = *data - word_start;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+parse_ext_substmt(struct ly_ctx *ctx, const char **data, char *word, int word_len,
+ struct lysp_stmt **child)
+{
+ char *buf;
+ LY_ERR ret = 0;
+ enum yang_keyword kw;
+ struct lysp_stmt *stmt, *par_child;
+
+ stmt = calloc(1, sizeof *stmt);
+ LY_CHECK_ERR_RET(!stmt, LOGMEM(NULL), LY_EMEM);
+
+ stmt->stmt = lydict_insert(ctx, word, word_len);
+
+ /* get optional argument */
+ ret = get_string(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ stmt->arg = lydict_insert_zc(ctx, word);
+ } else {
+ stmt->arg = lydict_insert(ctx, word, word_len);
+ }
+
+ /* insert into parent statements */
+ if (!*child) {
+ *child = stmt;
+ } else {
+ for (par_child = *child; par_child->next; par_child = par_child->next);
+ par_child->next = stmt;
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ ret = parse_ext_substmt(ctx, data, word, word_len, &stmt->child);
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_ext(struct ly_ctx *ctx, const char **data, const char *ext_name, int ext_name_len, LYEXT_SUBSTMT insubstmt,
+ uint32_t insubstmt_index, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ struct lysp_ext_instance *e;
+ enum yang_keyword kw;
+
+ LYSP_ARRAY_NEW_RET(ctx, exts, e, LY_EMEM);
+
+ /* store name and insubstmt info */
+ e->name = lydict_insert(ctx, ext_name, ext_name_len);
+ e->insubstmt = insubstmt;
+ e->insubstmt_index = insubstmt_index;
+
+ /* get optional argument */
+ ret = get_string(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ e->argument = lydict_insert_zc(ctx, word);
+ } else {
+ e->argument = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ ret = parse_ext_substmt(ctx, data, word, word_len, &e->child);
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_text_field(struct ly_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, uint32_t substmt_index,
+ const char **value, enum yang_arg arg, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*value) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, substmt2str(substmt));
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, arg, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ /* store value and spend buf if allocated */
+ if (buf) {
+ *value = lydict_insert_zc(ctx, word);
+ } else {
+ *value = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, substmt, substmt_index, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), substmt2str(substmt));
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_yangversion(struct ly_ctx *ctx, const char **data, struct lysp_module *mod)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (mod->version) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "yang-version");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len == 3) && !strncmp(word, "1.0", word_len)) {
+ mod->version = LYS_VERSION_1_0;
+ } else if ((word_len == 3) && !strncmp(word, "1.1", word_len)) {
+ mod->version = LYS_VERSION_1_1;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "yang-version");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_VERSION, 0, &mod->exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "yang-version");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_belongsto(struct ly_ctx *ctx, const char **data, const char **belongsto, const char **prefix, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*belongsto) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "belongs-to");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ *belongsto = lydict_insert_zc(ctx, word);
+ } else {
+ *belongsto = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_PREFIX:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, prefix, Y_IDENTIF_ARG, exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_BELONGSTO, 0, exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "belongs-to");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ /* mandatory substatements */
+ if (!*prefix) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "prefix", "belongs-to");
+ return LY_EVALID;
+ }
+
+ return ret;
+}
+
+static LY_ERR
+parse_revisiondate(struct ly_ctx *ctx, const char **data, char *rev, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (rev[0]) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "revision-date");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ /* check value */
+ if (lysp_check_date(ctx, word, word_len, "revision-date")) {
+ free(buf);
+ return LY_EVALID;
+ }
+
+ /* store value and spend buf if allocated */
+ strncpy(rev, word, word_len);
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REVISIONDATE, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "revision-date");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_include(struct ly_ctx *ctx, const char **data, struct lysp_include **includes)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_include *inc;
+
+ LYSP_ARRAY_NEW_RET(ctx, includes, inc, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ inc->name = lydict_insert_zc(ctx, word);
+ } else {
+ inc->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &inc->dsc, Y_STR_ARG, &inc->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &inc->ref, Y_STR_ARG, &inc->exts);
+ break;
+ case YANG_REVISION_DATE:
+ ret = parse_revisiondate(ctx, data, inc->rev, &inc->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inc->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "include");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_import(struct ly_ctx *ctx, const char **data, struct lysp_import **imports)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_import *imp;
+
+ LYSP_ARRAY_NEW_RET(ctx, imports, imp, LY_EVALID);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ imp->name = lydict_insert_zc(ctx, word);
+ } else {
+ imp->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_PREFIX:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &imp->prefix, Y_IDENTIF_ARG, &imp->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &imp->dsc, Y_STR_ARG, &imp->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &imp->ref, Y_STR_ARG, &imp->exts);
+ break;
+ case YANG_REVISION_DATE:
+ ret = parse_revisiondate(ctx, data, imp->rev, &imp->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &imp->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "import");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ /* mandatory substatements */
+ if (!imp->prefix) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "prefix", "import");
+ return LY_EVALID;
+ }
+
+ return ret;
+}
+
+static LY_ERR
+parse_revision(struct ly_ctx *ctx, const char **data, struct lysp_revision **revs)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_revision *rev;
+
+ LYSP_ARRAY_NEW_RET(ctx, revs, rev, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ /* check value */
+ if (lysp_check_date(ctx, word, word_len, "revision")) {
+ return LY_EVALID;
+ }
+
+ strncpy(rev->rev, word, word_len);
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rev->dsc, Y_STR_ARG, &rev->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rev->ref, Y_STR_ARG, &rev->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rev->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "revision");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_text_fields(struct ly_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, const char ***texts, enum yang_arg arg,
+ struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int count, word_len;
+ enum yang_keyword kw;
+
+ /* allocate new pointer */
+ for (count = 1; (*texts) && (*texts)[count - 1]; ++count);
+ *texts = realloc(*texts, count * sizeof **texts);
+ LY_CHECK_ERR_RET(!*texts, LOGMEM(ctx), LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, arg, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ (*texts)[count - 1] = lydict_insert_zc(ctx, word);
+ } else {
+ (*texts)[count - 1] = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, substmt, count - 1, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), substmt2str(substmt));
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_config(struct ly_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_CONFIG_MASK) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "config");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len == 4) && !strncmp(word, "true", word_len)) {
+ *flags |= LYS_CONFIG_W;
+ } else if ((word_len == 5) && !strncmp(word, "false", word_len)) {
+ *flags |= LYS_CONFIG_R;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "config");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_CONFIG, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "config");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_mandatory(struct ly_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_MAND_MASK) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "mandatory");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len == 4) && !strncmp(word, "true", word_len)) {
+ *flags |= LYS_MAND_TRUE;
+ } else if ((word_len == 5) && !strncmp(word, "false", word_len)) {
+ *flags |= LYS_MAND_FALSE;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "mandatory");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MANDATORY, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "mandatory");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_restr(struct ly_ctx *ctx, const char **data, enum yang_keyword restr_kw, struct lysp_restr *restr)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ restr->arg = lydict_insert_zc(ctx, word);
+ } else {
+ restr->arg = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts);
+ break;
+ case YANG_ERROR_APP_TAG:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts);
+ break;
+ case YANG_ERROR_MESSAGE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), kw2str(restr_kw));
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_restrs(struct ly_ctx *ctx, const char **data, enum yang_keyword restr_kw, struct lysp_restr **restrs)
+{
+ struct lysp_restr *restr;
+
+ LYSP_ARRAY_NEW_RET(ctx, restrs, restr, LY_EMEM);
+
+ return parse_restr(ctx, data, restr_kw, restr);
+}
+
+static LY_ERR
+parse_status(struct ly_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_STATUS_MASK) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "status");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len == 7) && !strncmp(word, "current", word_len)) {
+ *flags |= LYS_STATUS_CURR;
+ } else if ((word_len == 10) && !strncmp(word, "deprecated", word_len)) {
+ *flags |= LYS_STATUS_DEPRC;
+ } else if ((word_len == 8) && !strncmp(word, "obsolete", word_len)) {
+ *flags |= LYS_STATUS_OBSLT;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "status");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_STATUS, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "status");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_when(struct ly_ctx *ctx, const char **data, struct lysp_when **when_p)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_when *when;
+
+ if (*when_p) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "when");
+ return LY_EVALID;
+ }
+
+ when = calloc(1, sizeof *when);
+ LY_CHECK_ERR_RET(!when, LOGMEM(ctx), LY_EMEM);
+ *when_p = when;
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ when->cond = lydict_insert_zc(ctx, word);
+ } else {
+ when->cond = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &when->dsc, Y_STR_ARG, &when->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &when->ref, Y_STR_ARG, &when->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &when->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "when");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_any(struct ly_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ struct lysp_node *iter;
+ struct lysp_node_anydata *any;
+
+ /* create structure */
+ any = calloc(1, sizeof *any);
+ LY_CHECK_ERR_RET(!any, LOGMEM(ctx), LY_EMEM);
+ any->nodetype = kw == YANG_ANYDATA ? LYS_ANYDATA : LYS_ANYXML;
+
+ /* insert into siblings */
+ if (!*siblings) {
+ *siblings = (struct lysp_node *)any;
+ } else {
+ for (iter = *siblings; iter->next; iter = iter->next);
+ iter->next = (struct lysp_node *)any;
+ }
+
+ /* get name */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ any->name = lydict_insert_zc(ctx, word);
+ } else {
+ any->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CONFIG:
+ ret = parse_config(ctx, data, &any->flags, &any->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &any->dsc, Y_STR_ARG, &any->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &any->iffeatures, Y_STR_ARG, &any->exts);
+ break;
+ case YANG_MANDATORY:
+ ret = parse_mandatory(ctx, data, &any->flags, &any->exts);
+ break;
+ case YANG_MUST:
+ ret = parse_restrs(ctx, data, kw, &any->musts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &any->ref, Y_STR_ARG, &any->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &any->flags, &any->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &any->when);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &any->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), nodetype2str(any->nodetype));
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_type_enum_value_pos(struct ly_ctx *ctx, const char **data, enum yang_keyword val_kw, int64_t *value, uint16_t *flags,
+ struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word, *ptr;
+ int word_len;
+ long int num;
+ unsigned long int unum;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_SET_VALUE) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, kw2str(val_kw));
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_VALUE;
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (!word_len || (word[0] == '+') || ((word[0] == '0') && (word_len > 1)) || ((val_kw == YANG_VALUE) && !strncmp(word, "-0", 2))) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, kw2str(val_kw));
+ free(buf);
+ return LY_EVALID;
+ }
+
+ errno = 0;
+ if (val_kw == YANG_VALUE) {
+ num = strtol(word, &ptr, 10);
+ } else {
+ unum = strtoul(word, &ptr, 10);
+ }
+ /* we have not parsed the whole argument */
+ if (ptr - word != word_len) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, kw2str(val_kw));
+ free(buf);
+ return LY_EVALID;
+ }
+ if (errno == ERANGE) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_OOB, word_len, word, kw2str(val_kw));
+ free(buf);
+ return LY_EVALID;
+ }
+ if (val_kw == YANG_VALUE) {
+ *value = num;
+ } else {
+ *value = unum;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, val_kw == YANG_VALUE ? LYEXT_SUBSTMT_VALUE : LYEXT_SUBSTMT_POSITION, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), kw2str(val_kw));
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_type_enum(struct ly_ctx *ctx, const char **data, enum yang_keyword enum_kw, struct lysp_type_enum **enums)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_type_enum *enm;
+
+ LYSP_ARRAY_NEW_RET(ctx, enums, enm, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ enm->name = lydict_insert_zc(ctx, word);
+ } else {
+ enm->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &enm->dsc, Y_STR_ARG, &enm->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &enm->iffeatures, Y_STR_ARG, &enm->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &enm->ref, Y_STR_ARG, &enm->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &enm->flags, &enm->exts);
+ break;
+ case YANG_VALUE:
+ case YANG_POSITION:
+ ret = parse_type_enum_value_pos(ctx, data, kw, &enm->value, &enm->flags, &enm->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &enm->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), kw2str(enum_kw));
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_type_fracdigits(struct ly_ctx *ctx, const char **data, uint8_t *fracdig, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word, *ptr;
+ int word_len;
+ unsigned long int num;
+ enum yang_keyword kw;
+
+ if (*fracdig) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "fraction-digits");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (!word_len || (word[0] == '0') || !isdigit(word[0])) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+ free(buf);
+ return LY_EVALID;
+ }
+
+ errno = 0;
+ num = strtoul(word, &ptr, 10);
+ /* we have not parsed the whole argument */
+ if (ptr - word != word_len) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+ free(buf);
+ return LY_EVALID;
+ }
+ if ((errno == ERANGE) || (num > 18)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_OOB, word_len, word, "fraction-digits");
+ free(buf);
+ return LY_EVALID;
+ }
+ *fracdig = num;
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_FRACDIGITS, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "fraction-digits");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_type_reqinstance(struct ly_ctx *ctx, const char **data, uint8_t *reqinst, uint16_t *flags,
+ struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_SET_REQINST) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "require-instance");
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_REQINST;
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len == 4) && !strncmp(word, "true", word_len)) {
+ *reqinst = 1;
+ } else if ((word_len != 5) || strncmp(word, "false", word_len)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "require-instance");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REQINSTANCE, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "require-instance");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_type_pattern_modifier(struct ly_ctx *ctx, const char **data, const char **pat, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if ((*pat)[0] == 0x15) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "modifier");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len != 12) || strncmp(word, "invert-match", word_len)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "modifier");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ /* replace the value in the dictionary */
+ buf = malloc(strlen(*pat) + 1);
+ LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
+ strcpy(buf, *pat);
+ lydict_remove(ctx, *pat);
+
+ assert(buf[0] == 0x06);
+ buf[0] = 0x15;
+ *pat = lydict_insert_zc(ctx, buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MODIFIER, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "modifier");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_type_pattern(struct ly_ctx *ctx, const char **data, struct lysp_restr **patterns)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_restr *restr;
+
+ LYSP_ARRAY_NEW_RET(ctx, patterns, restr, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ /* add special meaning first byte */
+ if (buf) {
+ buf = realloc(buf, word_len + 2);
+ word = buf;
+ } else {
+ buf = malloc(word_len + 2);
+ }
+ LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
+ memmove(buf + 1, word, word_len + 1);
+ word[0] = 0x06;
+ restr->arg = lydict_insert_zc(ctx, word);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts);
+ break;
+ case YANG_ERROR_APP_TAG:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts);
+ break;
+ case YANG_ERROR_MESSAGE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts);
+ break;
+ case YANG_MODIFIER:
+ ret = parse_type_pattern_modifier(ctx, data, &restr->arg, &restr->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "pattern");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_type(struct ly_ctx *ctx, const char **data, struct lysp_type *type)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_type *nest_type;
+
+ if (type->name) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "type");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ type->name = lydict_insert_zc(ctx, word);
+ } else {
+ type->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_BASE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &type->bases, Y_PREF_IDENTIF_ARG, &type->exts);
+ break;
+ case YANG_BIT:
+ ret = parse_type_enum(ctx, data, kw, &type->bits);
+ break;
+ case YANG_ENUM:
+ ret = parse_type_enum(ctx, data, kw, &type->enums);
+ break;
+ case YANG_FRACTION_DIGITS:
+ ret = parse_type_fracdigits(ctx, data, &type->fraction_digits, &type->exts);
+ break;
+ case YANG_LENGTH:
+ if (type->length) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, kw2str(kw));
+ return LY_EVALID;
+ }
+ type->length = calloc(1, sizeof *type->length);
+ LY_CHECK_ERR_RET(!type->length, LOGMEM(ctx), LY_EMEM);
+
+ ret = parse_restr(ctx, data, kw, type->length);
+ break;
+ case YANG_PATH:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PATH, 0, &type->path, Y_STR_ARG, &type->exts);
+ break;
+ case YANG_PATTERN:
+ ret = parse_type_pattern(ctx, data, &type->patterns);
+ break;
+ case YANG_RANGE:
+ if (type->range) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, kw2str(kw));
+ return LY_EVALID;
+ }
+ type->range = calloc(1, sizeof *type->range);
+ LY_CHECK_ERR_RET(!type->range, LOGMEM(ctx), LY_EVALID);
+
+ ret = parse_restr(ctx, data, kw, type->range);
+ break;
+ case YANG_REQUIRE_INSTANCE:
+ ret = parse_type_reqinstance(ctx, data, &type->require_instance, &type->flags, &type->exts);
+ break;
+ case YANG_TYPE:
+ {
+ LYSP_ARRAY_NEW_RET(ctx, &type->types, nest_type, LY_EMEM);
+ }
+ ret = parse_type(ctx, data, nest_type);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &type->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "when");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_leaf(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_node *iter;
+ struct lysp_node_leaf *leaf;
+
+ /* create structure */
+ leaf = calloc(1, sizeof *leaf);
+ LY_CHECK_ERR_RET(!leaf, LOGMEM(ctx), LY_EMEM);
+ leaf->nodetype = LYS_LEAF;
+
+ /* insert into siblings */
+ if (!*siblings) {
+ *siblings = (struct lysp_node *)leaf;
+ } else {
+ for (iter = *siblings; iter->next; iter = iter->next);
+ iter->next = (struct lysp_node *)leaf;
+ }
+
+ /* get name */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ leaf->name = lydict_insert_zc(ctx, word);
+ } else {
+ leaf->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CONFIG:
+ ret = parse_config(ctx, data, &leaf->flags, &leaf->exts);
+ break;
+ case YANG_DEFAULT:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &leaf->dflt, Y_STR_ARG, &leaf->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &leaf->dsc, Y_STR_ARG, &leaf->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &leaf->iffeatures, Y_STR_ARG, &leaf->exts);
+ break;
+ case YANG_MANDATORY:
+ ret = parse_mandatory(ctx, data, &leaf->flags, &leaf->exts);
+ break;
+ case YANG_MUST:
+ ret = parse_restrs(ctx, data, kw, &leaf->musts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &leaf->ref, Y_STR_ARG, &leaf->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &leaf->flags, &leaf->exts);
+ break;
+ case YANG_TYPE:
+ ret = parse_type(ctx, data, &leaf->type);
+ break;
+ case YANG_UNITS:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &leaf->units, Y_STR_ARG, &leaf->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &leaf->when);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &leaf->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "leaf");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ /* mandatory substatements */
+ if (!leaf->type.name) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "type", "leaf");
+ return LY_EVALID;
+ }
+
+ return ret;
+}
+
+static LY_ERR
+parse_maxelements(struct ly_ctx *ctx, const char **data, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word, *ptr;
+ int word_len;
+ unsigned long int num;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_SET_MAX) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "max-elements");
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_MAX;
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (!word_len || (word[0] == '0') || ((word[0] != 'u') && !isdigit(word[0]))) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "max-elements");
+ free(buf);
+ return LY_EVALID;
+ }
+
+ if (strncmp(word, "unbounded", word_len)) {
+ errno = 0;
+ num = strtoul(word, &ptr, 10);
+ /* we have not parsed the whole argument */
+ if (ptr - word != word_len) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "max-elements");
+ free(buf);
+ return LY_EVALID;
+ }
+ if ((errno == ERANGE) || (num > UINT32_MAX)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_OOB, word_len, word, "max-elements");
+ free(buf);
+ return LY_EVALID;
+ }
+
+ *max = num;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MAX, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "max-elements");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_minelements(struct ly_ctx *ctx, const char **data, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word, *ptr;
+ int word_len;
+ unsigned long int num;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_SET_MIN) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "min-elements");
+ return LY_EVALID;
+ }
+ *flags |= LYS_SET_MIN;
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (!word_len || !isdigit(word[0]) || ((word[0] == '0') && (word_len > 1))) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "min-elements");
+ free(buf);
+ return LY_EVALID;
+ }
+
+ errno = 0;
+ num = strtoul(word, &ptr, 10);
+ /* we have not parsed the whole argument */
+ if (ptr - word != word_len) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "min-elements");
+ free(buf);
+ return LY_EVALID;
+ }
+ if ((errno == ERANGE) || (num > UINT32_MAX)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_OOB, word_len, word, "min-elements");
+ free(buf);
+ return LY_EVALID;
+ }
+ *min = num;
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MIN, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "min-elements");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_orderedby(struct ly_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_ORDBY_MASK) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "ordered-by");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len == 6) && !strncmp(word, "system", word_len)) {
+ *flags |= LYS_ORDBY_SYSTEM;
+ } else if ((word_len == 4) && !strncmp(word, "user", word_len)) {
+ *flags |= LYS_ORDBY_USER;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "ordered-by");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ORDEREDBY, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "ordered-by");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_leaflist(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_node *iter;
+ struct lysp_node_leaflist *llist;
+
+ /* create structure */
+ llist = calloc(1, sizeof *llist);
+ LY_CHECK_ERR_RET(!llist, LOGMEM(ctx), LY_EMEM);
+ llist->nodetype = LYS_LEAFLIST;
+
+ /* insert into siblings */
+ if (!*siblings) {
+ *siblings = (struct lysp_node *)llist;
+ } else {
+ for (iter = *siblings; iter->next; iter = iter->next);
+ iter->next = (struct lysp_node *)llist;
+ }
+
+ /* get name */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ llist->name = lydict_insert_zc(ctx, word);
+ } else {
+ llist->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CONFIG:
+ ret = parse_config(ctx, data, &llist->flags, &llist->exts);
+ break;
+ case YANG_DEFAULT:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &llist->dflts, Y_STR_ARG, &llist->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &llist->dsc, Y_STR_ARG, &llist->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &llist->iffeatures, Y_STR_ARG, &llist->exts);
+ break;
+ case YANG_MAX_ELEMENTS:
+ ret = parse_maxelements(ctx, data, &llist->max, &llist->flags, &llist->exts);
+ break;
+ case YANG_MIN_ELEMENTS:
+ ret = parse_minelements(ctx, data, &llist->min, &llist->flags, &llist->exts);
+ break;
+ case YANG_MUST:
+ ret = parse_restrs(ctx, data, kw, &llist->musts);
+ break;
+ case YANG_ORDERED_BY:
+ ret = parse_orderedby(ctx, data, &llist->flags, &llist->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &llist->ref, Y_STR_ARG, &llist->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &llist->flags, &llist->exts);
+ break;
+ case YANG_TYPE:
+ ret = parse_type(ctx, data, &llist->type);
+ break;
+ case YANG_UNITS:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &llist->units, Y_STR_ARG, &llist->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &llist->when);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &llist->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "llist");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ /* mandatory substatements */
+ if (!llist->type.name) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "type", "leaf-list");
+ return LY_EVALID;
+ }
+
+ return ret;
+}
+
+static LY_ERR
+parse_refine(struct ly_ctx *ctx, const char **data, struct lysp_refine **refines)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_refine *rf;
+
+ LYSP_ARRAY_NEW_RET(ctx, refines, rf, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ rf->nodeid = lydict_insert_zc(ctx, word);
+ } else {
+ rf->nodeid = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CONFIG:
+ ret = parse_config(ctx, data, &rf->flags, &rf->exts);
+ break;
+ case YANG_DEFAULT:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &rf->dflts, Y_STR_ARG, &rf->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rf->dsc, Y_STR_ARG, &rf->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &rf->iffeatures, Y_STR_ARG, &rf->exts);
+ break;
+ case YANG_MAX_ELEMENTS:
+ ret = parse_maxelements(ctx, data, &rf->max, &rf->flags, &rf->exts);
+ break;
+ case YANG_MIN_ELEMENTS:
+ ret = parse_minelements(ctx, data, &rf->min, &rf->flags, &rf->exts);
+ break;
+ case YANG_MUST:
+ ret = parse_restrs(ctx, data, kw, &rf->musts);
+ break;
+ case YANG_MANDATORY:
+ ret = parse_mandatory(ctx, data, &rf->flags, &rf->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rf->ref, Y_STR_ARG, &rf->exts);
+ break;
+ case YANG_PRESENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &rf->presence, Y_STR_ARG, &rf->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rf->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "refine");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_typedef(struct ly_ctx *ctx, const char **data, struct lysp_tpdf **typedefs)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_tpdf *tpdf;
+
+ LYSP_ARRAY_NEW_RET(ctx, typedefs, tpdf, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ tpdf->name = lydict_insert_zc(ctx, word);
+ } else {
+ tpdf->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DEFAULT:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &tpdf->dflt, Y_STR_ARG, &tpdf->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &tpdf->dsc, Y_STR_ARG, &tpdf->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &tpdf->ref, Y_STR_ARG, &tpdf->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &tpdf->flags, &tpdf->exts);
+ break;
+ case YANG_TYPE:
+ ret = parse_type(ctx, data, &tpdf->type);
+ break;
+ case YANG_UNITS:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &tpdf->units, Y_STR_ARG, &tpdf->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &tpdf->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "typedef");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ /* mandatory substatements */
+ if (!tpdf->type.name) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "type", "typedef");
+ return LY_EVALID;
+ }
+
+ return ret;
+}
+
+static LY_ERR
+parse_inout(struct ly_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_action_inout **inout_p)
+{
+ LY_ERR ret = 0;
+ char *word;
+ int word_len;
+ struct lysp_action_inout *inout;
+
+ if (*inout_p) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, kw2str(kw));
+ return LY_EVALID;
+ }
+
+ /* create structure */
+ inout = calloc(1, sizeof *inout);
+ LY_CHECK_ERR_RET(!inout, LOGMEM(ctx), LY_EMEM);
+ *inout_p = inout;
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, &inout->data);
+ break;
+ case YANG_CHOICE:
+ ret = parse_choice(ctx, data, &inout->data);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, &inout->data);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, &inout->data);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, &inout->data);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, &inout->data);
+ break;
+ case YANG_USES:
+ ret = parse_uses(ctx, data, &inout->data);
+ break;
+
+ case YANG_TYPEDEF:
+ ret = parse_typedef(ctx, data, &inout->typedefs);
+ break;
+ case YANG_MUST:
+ ret = parse_restrs(ctx, data, kw, &inout->musts);
+ break;
+ case YANG_GROUPING:
+ ret = parse_grouping(ctx, data, &inout->groupings);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inout->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "input/output");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_action(struct ly_ctx *ctx, const char **data, struct lysp_action **actions)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_action *act;
+
+ LYSP_ARRAY_NEW_RET(ctx, actions, act, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ act->name = lydict_insert_zc(ctx, word);
+ } else {
+ act->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &act->dsc, Y_STR_ARG, &act->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &act->iffeatures, Y_STR_ARG, &act->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &act->ref, Y_STR_ARG, &act->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &act->flags, &act->exts);
+ break;
+
+ case YANG_INPUT:
+ ret = parse_inout(ctx, data, kw, &act->input);
+ break;
+ case YANG_OUTPUT:
+ ret = parse_inout(ctx, data, kw, &act->output);
+ break;
+
+ case YANG_TYPEDEF:
+ ret = parse_typedef(ctx, data, &act->typedefs);
+ break;
+ case YANG_GROUPING:
+ ret = parse_grouping(ctx, data, &act->groupings);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &act->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "action");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_notif(struct ly_ctx *ctx, const char **data, struct lysp_notif **notifs)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_notif *notif;
+
+ LYSP_ARRAY_NEW_RET(ctx, notifs, notif, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ notif->name = lydict_insert_zc(ctx, word);
+ } else {
+ notif->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, ¬if->dsc, Y_STR_ARG, ¬if->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, ¬if->iffeatures, Y_STR_ARG, ¬if->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, ¬if->ref, Y_STR_ARG, ¬if->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, ¬if->flags, ¬if->exts);
+ break;
+
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, ¬if->data);
+ break;
+ case YANG_CHOICE:
+ ret = parse_case(ctx, data, ¬if->data);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, ¬if->data);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, ¬if->data);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, ¬if->data);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, ¬if->data);
+ break;
+ case YANG_USES:
+ ret = parse_uses(ctx, data, ¬if->data);
+ break;
+
+ case YANG_MUST:
+ ret = parse_restrs(ctx, data, kw, ¬if->musts);
+ break;
+ case YANG_TYPEDEF:
+ ret = parse_typedef(ctx, data, ¬if->typedefs);
+ break;
+ case YANG_GROUPING:
+ ret = parse_grouping(ctx, data, ¬if->groupings);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, ¬if->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "notification");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_grouping(struct ly_ctx *ctx, const char **data, struct lysp_grp **groupings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_grp *grp;
+
+ LYSP_ARRAY_NEW_RET(ctx, groupings, grp, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ grp->name = lydict_insert_zc(ctx, word);
+ } else {
+ grp->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &grp->dsc, Y_STR_ARG, &grp->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &grp->ref, Y_STR_ARG, &grp->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &grp->flags, &grp->exts);
+ break;
+
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, &grp->data);
+ break;
+ case YANG_CHOICE:
+ ret = parse_choice(ctx, data, &grp->data);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, &grp->data);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, &grp->data);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, &grp->data);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, &grp->data);
+ break;
+ case YANG_USES:
+ ret = parse_uses(ctx, data, &grp->data);
+ break;
+
+ case YANG_TYPEDEF:
+ ret = parse_typedef(ctx, data, &grp->typedefs);
+ break;
+ case YANG_ACTION:
+ ret = parse_action(ctx, data, &grp->actions);
+ break;
+ case YANG_GROUPING:
+ ret = parse_grouping(ctx, data, &grp->groupings);
+ break;
+ case YANG_NOTIFICATION:
+ ret = parse_notif(ctx, data, &grp->notifs);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &grp->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "augment");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_augment(struct ly_ctx *ctx, const char **data, struct lysp_augment **augments)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_augment *aug;
+
+ LYSP_ARRAY_NEW_RET(ctx, augments, aug, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ aug->nodeid = lydict_insert_zc(ctx, word);
+ } else {
+ aug->nodeid = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &aug->dsc, Y_STR_ARG, &aug->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &aug->iffeatures, Y_STR_ARG, &aug->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &aug->ref, Y_STR_ARG, &aug->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &aug->flags, &aug->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &aug->when);
+ break;
+
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, &aug->child);
+ break;
+ case YANG_CASE:
+ ret = parse_case(ctx, data, &aug->child);
+ break;
+ case YANG_CHOICE:
+ ret = parse_choice(ctx, data, &aug->child);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, &aug->child);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, &aug->child);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, &aug->child);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, &aug->child);
+ break;
+ case YANG_USES:
+ ret = parse_uses(ctx, data, &aug->child);
+ break;
+
+ case YANG_ACTION:
+ ret = parse_action(ctx, data, &aug->actions);
+ break;
+ case YANG_NOTIFICATION:
+ ret = parse_notif(ctx, data, &aug->notifs);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &aug->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "augment");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_uses(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_node *iter;
+ struct lysp_node_uses *uses;
+
+ /* create structure */
+ uses = calloc(1, sizeof *uses);
+ LY_CHECK_ERR_RET(!uses, LOGMEM(ctx), LY_EMEM);
+ uses->nodetype = LYS_USES;
+
+ /* insert into siblings */
+ if (!*siblings) {
+ *siblings = (struct lysp_node *)uses;
+ } else {
+ for (iter = *siblings; iter->next; iter = iter->next);
+ iter->next = (struct lysp_node *)uses;
+ }
+
+ /* get name */
+ ret = get_string(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ uses->name = lydict_insert_zc(ctx, word);
+ } else {
+ uses->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &uses->dsc, Y_STR_ARG, &uses->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &uses->iffeatures, Y_STR_ARG, &uses->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &uses->ref, Y_STR_ARG, &uses->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &uses->flags, &uses->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &uses->when);
+ break;
+
+ case YANG_REFINE:
+ ret = parse_refine(ctx, data, &uses->refines);
+ break;
+ case YANG_AUGMENT:
+ ret = parse_augment(ctx, data, &uses->augments);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &uses->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "uses");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_case(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_node *iter;
+ struct lysp_node_case *cas;
+
+ /* create structure */
+ cas = calloc(1, sizeof *cas);
+ LY_CHECK_ERR_RET(!cas, LOGMEM(ctx), LY_EMEM);
+ cas->nodetype = LYS_CASE;
+
+ /* insert into siblings */
+ if (!*siblings) {
+ *siblings = (struct lysp_node *)cas;
+ } else {
+ for (iter = *siblings; iter->next; iter = iter->next);
+ iter->next = (struct lysp_node *)cas;
+ }
+
+ /* get name */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ cas->name = lydict_insert_zc(ctx, word);
+ } else {
+ cas->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cas->dsc, Y_STR_ARG, &cas->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cas->iffeatures, Y_STR_ARG, &cas->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cas->ref, Y_STR_ARG, &cas->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &cas->flags, &cas->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &cas->when);
+ break;
+
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, &cas->child);
+ break;
+ case YANG_CHOICE:
+ ret = parse_case(ctx, data, &cas->child);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, &cas->child);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, &cas->child);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, &cas->child);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, &cas->child);
+ break;
+ case YANG_USES:
+ ret = parse_uses(ctx, data, &cas->child);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cas->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "case");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_choice(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_node *iter;
+ struct lysp_node_choice *choic;
+
+ /* create structure */
+ choic = calloc(1, sizeof *choic);
+ LY_CHECK_ERR_RET(!choic, LOGMEM(ctx), LY_EMEM);
+ choic->nodetype = LYS_CHOICE;
+
+ /* insert into siblings */
+ if (!*siblings) {
+ *siblings = (struct lysp_node *)choic;
+ } else {
+ for (iter = *siblings; iter->next; iter = iter->next);
+ iter->next = (struct lysp_node *)choic;
+ }
+
+ /* get name */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ choic->name = lydict_insert_zc(ctx, word);
+ } else {
+ choic->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CONFIG:
+ ret = parse_config(ctx, data, &choic->flags, &choic->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &choic->dsc, Y_STR_ARG, &choic->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &choic->iffeatures, Y_STR_ARG, &choic->exts);
+ break;
+ case YANG_MANDATORY:
+ ret = parse_mandatory(ctx, data, &choic->flags, &choic->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &choic->ref, Y_STR_ARG, &choic->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &choic->flags, &choic->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &choic->when);
+ break;
+ case YANG_DEFAULT:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &choic->dflt, Y_IDENTIF_ARG, &choic->exts);
+ break;
+
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, &choic->child);
+ break;
+ case YANG_CASE:
+ ret = parse_case(ctx, data, &choic->child);
+ break;
+ case YANG_CHOICE:
+ ret = parse_choice(ctx, data, &choic->child);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, &choic->child);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, &choic->child);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, &choic->child);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, &choic->child);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &choic->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "choice");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_container(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_node *iter;
+ struct lysp_node_container *cont;
+
+ /* create structure */
+ cont = calloc(1, sizeof *cont);
+ LY_CHECK_ERR_RET(!cont, LOGMEM(ctx), LY_EMEM);
+ cont->nodetype = LYS_CONTAINER;
+
+ /* insert into siblings */
+ if (!*siblings) {
+ *siblings = (struct lysp_node *)cont;
+ } else {
+ for (iter = *siblings; iter->next; iter = iter->next);
+ iter->next = (struct lysp_node *)cont;
+ }
+
+ /* get name */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ cont->name = lydict_insert_zc(ctx, word);
+ } else {
+ cont->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CONFIG:
+ ret = parse_config(ctx, data, &cont->flags, &cont->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cont->dsc, Y_STR_ARG, &cont->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cont->iffeatures, Y_STR_ARG, &cont->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cont->ref, Y_STR_ARG, &cont->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &cont->flags, &cont->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &cont->when);
+ break;
+ case YANG_PRESENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &cont->presence, Y_STR_ARG, &cont->exts);
+ break;
+
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, &cont->child);
+ break;
+ case YANG_CHOICE:
+ ret = parse_choice(ctx, data, &cont->child);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, &cont->child);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, &cont->child);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, &cont->child);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, &cont->child);
+ break;
+ case YANG_USES:
+ ret = parse_uses(ctx, data, &cont->child);
+ break;
+
+ case YANG_TYPEDEF:
+ ret = parse_typedef(ctx, data, &cont->typedefs);
+ break;
+ case YANG_MUST:
+ ret = parse_restrs(ctx, data, kw, &cont->musts);
+ break;
+ case YANG_ACTION:
+ ret = parse_action(ctx, data, &cont->actions);
+ break;
+ case YANG_GROUPING:
+ ret = parse_grouping(ctx, data, &cont->groupings);
+ break;
+ case YANG_NOTIFICATION:
+ ret = parse_notif(ctx, data, &cont->notifs);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cont->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "container");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_list(struct ly_ctx *ctx, const char **data, struct lysp_node **siblings)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_node *iter;
+ struct lysp_node_list *list;
+
+ /* create structure */
+ list = calloc(1, sizeof *list);
+ LY_CHECK_ERR_RET(!list, LOGMEM(ctx), LY_EMEM);
+ list->nodetype = LYS_LIST;
+
+ /* insert into siblings */
+ if (!*siblings) {
+ *siblings = (struct lysp_node *)list;
+ } else {
+ for (iter = *siblings; iter->next; iter = iter->next);
+ iter->next = (struct lysp_node *)list;
+ }
+
+ /* get name */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ list->name = lydict_insert_zc(ctx, word);
+ } else {
+ list->name = lydict_insert(ctx, word, word_len);
+ }
+
+ /* parse substatements */
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CONFIG:
+ ret = parse_config(ctx, data, &list->flags, &list->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &list->dsc, Y_STR_ARG, &list->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &list->iffeatures, Y_STR_ARG, &list->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &list->ref, Y_STR_ARG, &list->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &list->flags, &list->exts);
+ break;
+ case YANG_WHEN:
+ ret = parse_when(ctx, data, &list->when);
+ break;
+ case YANG_KEY:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_KEY, 0, &list->key, Y_STR_ARG, &list->exts);
+ break;
+ case YANG_MAX_ELEMENTS:
+ ret = parse_maxelements(ctx, data, &list->max, &list->flags, &list->exts);
+ break;
+ case YANG_MIN_ELEMENTS:
+ ret = parse_minelements(ctx, data, &list->min, &list->flags, &list->exts);
+ break;
+ case YANG_ORDERED_BY:
+ ret = parse_orderedby(ctx, data, &list->flags, &list->exts);
+ break;
+ case YANG_UNIQUE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, &list->uniques, Y_STR_ARG, &list->exts);
+ break;
+
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, &list->child);
+ break;
+ case YANG_CHOICE:
+ ret = parse_choice(ctx, data, &list->child);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, &list->child);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, &list->child);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, &list->child);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, &list->child);
+ break;
+ case YANG_USES:
+ ret = parse_uses(ctx, data, &list->child);
+ break;
+
+ case YANG_TYPEDEF:
+ ret = parse_typedef(ctx, data, &list->typedefs);
+ break;
+ case YANG_MUST:
+ ret = parse_restrs(ctx, data, kw, &list->musts);
+ break;
+ case YANG_ACTION:
+ ret = parse_action(ctx, data, &list->actions);
+ break;
+ case YANG_GROUPING:
+ ret = parse_grouping(ctx, data, &list->groupings);
+ break;
+ case YANG_NOTIFICATION:
+ ret = parse_notif(ctx, data, &list->notifs);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &list->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "container");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_yinelement(struct ly_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*flags & LYS_YINELEM_MASK) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "yin-element");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len == 4) && !strncmp(word, "true", word_len)) {
+ *flags |= LYS_YINELEM_TRUE;
+ } else if ((word_len == 5) && !strncmp(word, "false", word_len)) {
+ *flags |= LYS_YINELEM_FALSE;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "yin-element");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_YINELEM, 0, exts);
+ YANG_ERR_RET(ret);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "yin-element");
+ return LY_EVALID;
+ }
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_argument(struct ly_ctx *ctx, const char **data, const char **argument, uint16_t *flags, struct lysp_ext_instance **exts)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+
+ if (*argument) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_DUPSTMT, "argument");
+ return LY_EVALID;
+ }
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ *argument = lydict_insert_zc(ctx, word);
+ } else {
+ *argument = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_YIN_ELEMENT:
+ ret = parse_yinelement(ctx, data, flags, exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ARGUMENT, 0, exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "argument");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_extension(struct ly_ctx *ctx, const char **data, struct lysp_ext **extensions)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_ext *ex;
+
+ LYSP_ARRAY_NEW_RET(ctx, extensions, ex, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ ex->name = lydict_insert_zc(ctx, word);
+ } else {
+ ex->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ex->dsc, Y_STR_ARG, &ex->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ex->ref, Y_STR_ARG, &ex->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &ex->flags, &ex->exts);
+ break;
+ case YANG_ARGUMENT:
+ ret = parse_argument(ctx, data, &ex->argument, &ex->flags, &ex->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ex->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "extension");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_deviate(struct ly_ctx *ctx, const char **data, struct lysp_deviate **deviates)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len, dev_mod;
+ enum yang_keyword kw;
+ struct lysp_deviate *iter, *d;
+ struct lysp_deviate_add *d_add = NULL;
+ struct lysp_deviate_rpl *d_rpl = NULL;
+ struct lysp_deviate_del *d_del = NULL;
+ const char **d_units, ***d_uniques, ***d_dflts;
+ struct lysp_restr **d_musts;
+ uint16_t *d_flags;
+ uint32_t *d_min, *d_max;
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if ((word_len == 13) && !strncmp(word, "not-supported", word_len)) {
+ dev_mod = LYS_DEV_NOT_SUPPORTED;
+ } else if ((word_len == 3) && !strncmp(word, "add", word_len)) {
+ dev_mod = LYS_DEV_ADD;
+ } else if ((word_len == 7) && !strncmp(word, "replace", word_len)) {
+ dev_mod = LYS_DEV_REPLACE;
+ } else if ((word_len == 6) && !strncmp(word, "delete", word_len)) {
+ dev_mod = LYS_DEV_DELETE;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, word_len, word, "deviate");
+ free(buf);
+ return LY_EVALID;
+ }
+ free(buf);
+
+ /* create structure */
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ d = calloc(1, sizeof *d);
+ LY_CHECK_ERR_RET(!d, LOGMEM(ctx), LY_EMEM);
+ break;
+ case LYS_DEV_ADD:
+ d_add = calloc(1, sizeof *d_add);
+ LY_CHECK_ERR_RET(!d_add, LOGMEM(ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_add;
+ d_units = &d_add->units;
+ d_uniques = &d_add->uniques;
+ d_dflts = &d_add->dflts;
+ d_musts = &d_add->musts;
+ d_flags = &d_add->flags;
+ d_min = &d_add->min;
+ d_max = &d_add->max;
+ break;
+ case LYS_DEV_REPLACE:
+ d_rpl = calloc(1, sizeof *d_rpl);
+ LY_CHECK_ERR_RET(!d_rpl, LOGMEM(ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_rpl;
+ d_units = &d_rpl->units;
+ d_flags = &d_rpl->flags;
+ d_min = &d_rpl->min;
+ d_max = &d_rpl->max;
+ break;
+ case LYS_DEV_DELETE:
+ d_del = calloc(1, sizeof *d_del);
+ LY_CHECK_ERR_RET(!d_del, LOGMEM(ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_del;
+ d_units = &d_del->units;
+ d_uniques = &d_del->uniques;
+ d_dflts = &d_del->dflts;
+ d_musts = &d_del->musts;
+ d_flags = &d_del->flags;
+ d_min = &d_del->min;
+ d_max = &d_del->max;
+ break;
+ default:
+ assert(0);
+ LOGINT_RET(ctx);
+ }
+ d->mod = dev_mod;
+
+ /* insert into siblings */
+ if (!*deviates) {
+ *deviates = d;
+ } else {
+ for (iter = *deviates; iter->next; iter = iter->next);
+ iter->next = d;
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_CONFIG:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ default:
+ ret = parse_config(ctx, data, d_flags, &d->exts);
+ break;
+ }
+ break;
+ case YANG_DEFAULT:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ case LYS_DEV_REPLACE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &d_rpl->dflt, Y_STR_ARG, &d->exts);
+ break;
+ default:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, d_dflts, Y_STR_ARG, &d->exts);
+ break;
+ }
+ break;
+ case YANG_MANDATORY:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ default:
+ ret = parse_mandatory(ctx, data, d_flags, &d->exts);
+ break;
+ }
+ break;
+ case YANG_MAX_ELEMENTS:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ default:
+ ret = parse_maxelements(ctx, data, d_max, d_flags, &d->exts);
+ break;
+ }
+ break;
+ case YANG_MIN_ELEMENTS:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ default:
+ ret = parse_minelements(ctx, data, d_min, d_flags, &d->exts);
+ break;
+ }
+ break;
+ case YANG_MUST:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ default:
+ ret = parse_restrs(ctx, data, kw, d_musts);
+ break;
+ }
+ break;
+ case YANG_TYPE:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_ADD:
+ case LYS_DEV_DELETE:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ default:
+ d_rpl->type = calloc(1, sizeof *d_rpl->type);
+ LY_CHECK_ERR_RET(!d_rpl->type, LOGMEM(ctx), LY_EMEM);
+ ret = parse_type(ctx, data, d_rpl->type);
+ break;
+ }
+ break;
+ case YANG_UNIQUE:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ case LYS_DEV_REPLACE:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ default:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, d_uniques, Y_STR_ARG, &d->exts);
+ break;
+ }
+ break;
+ case YANG_UNITS:
+ switch (dev_mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INDEV, devmod2str(dev_mod), kw2str(kw));
+ return LY_EVALID;
+ default:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, d_units, Y_STR_ARG, &d->exts);
+ break;
+ }
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &d->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "deviate");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_deviation(struct ly_ctx *ctx, const char **data, struct lysp_deviation **deviations)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_deviation *dev;
+
+ LYSP_ARRAY_NEW_RET(ctx, deviations, dev, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ dev->nodeid = lydict_insert_zc(ctx, word);
+ } else {
+ dev->nodeid = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &dev->dsc, Y_STR_ARG, &dev->exts);
+ break;
+ case YANG_DEVIATE:
+ ret = parse_deviate(ctx, data, &dev->deviates);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &dev->ref, Y_STR_ARG, &dev->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &dev->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "deviation");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ /* mandatory substatements */
+ if (!dev->deviates) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "deviate", "deviation");
+ return LY_EVALID;
+ }
+
+ return ret;
+}
+
+static LY_ERR
+parse_feature(struct ly_ctx *ctx, const char **data, struct lysp_feature **features)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_feature *feat;
+
+ LYSP_ARRAY_NEW_RET(ctx, features, feat, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ feat->name = lydict_insert_zc(ctx, word);
+ } else {
+ feat->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &feat->dsc, Y_STR_ARG, &feat->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &feat->iffeatures, Y_STR_ARG, &feat->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &feat->ref, Y_STR_ARG, &feat->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &feat->flags, &feat->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &feat->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "feature");
+ return LY_EMEM;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_identity(struct ly_ctx *ctx, const char **data, struct lysp_ident **identities)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw;
+ struct lysp_ident *ident;
+
+ LYSP_ARRAY_NEW_RET(ctx, identities, ident, LY_EMEM);
+
+ /* get value */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ ident->name = lydict_insert_zc(ctx, word);
+ } else {
+ ident->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ident->dsc, Y_STR_ARG, &ident->exts);
+ break;
+ case YANG_IF_FEATURE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &ident->iffeatures, Y_STR_ARG, &ident->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ident->ref, Y_STR_ARG, &ident->exts);
+ break;
+ case YANG_STATUS:
+ ret = parse_status(ctx, data, &ident->flags, &ident->exts);
+ break;
+ case YANG_BASE:
+ ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &ident->bases, Y_PREF_IDENTIF_ARG, &ident->exts);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ident->exts);
+ break;
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "identity");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ return ret;
+}
+
+static LY_ERR
+parse_sub_module(struct ly_ctx *ctx, const char **data, struct lysp_module *mod)
+{
+ LY_ERR ret = 0;
+ char *buf, *word;
+ int word_len;
+ enum yang_keyword kw, prev_kw = 0;
+ enum yang_module_stmt mod_stmt = Y_MOD_MODULE_HEADER;
+
+ /* (sub)module name */
+ ret = get_string(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
+ YANG_ERR_RET(ret);
+
+ if (buf) {
+ mod->name = lydict_insert_zc(ctx, word);
+ } else {
+ mod->name = lydict_insert(ctx, word, word_len);
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
+ YANG_ERR_RET(ret);
+
+ switch (kw) {
+ /* module header */
+ case YANG_NAMESPACE:
+ case YANG_PREFIX:
+ if (mod->submodule) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "submodule");
+ return LY_EVALID;
+ }
+ /* fallthrough */
+ case YANG_BELONGS_TO:
+ if ((kw == YANG_BELONGS_TO) && !mod->submodule) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), "module");
+ return LY_EVALID;
+ }
+ /* fallthrough */
+ case YANG_YANG_VERSION:
+ if (mod_stmt > Y_MOD_MODULE_HEADER) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INORD, kw2str(kw), kw2str(prev_kw));
+ return LY_EVALID;
+ }
+ break;
+ /* linkage */
+ case YANG_INCLUDE:
+ case YANG_IMPORT:
+ if (mod_stmt > Y_MOD_LINKAGE) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INORD, kw2str(kw), kw2str(prev_kw));
+ return LY_EVALID;
+ }
+ mod_stmt = Y_MOD_LINKAGE;
+ break;
+ /* meta */
+ case YANG_ORGANIZATION:
+ case YANG_CONTACT:
+ case YANG_DESCRIPTION:
+ case YANG_REFERENCE:
+ if (mod_stmt > Y_MOD_META) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INORD, kw2str(kw), kw2str(prev_kw));
+ return LY_EVALID;
+ }
+ mod_stmt = Y_MOD_META;
+ break;
+
+ /* revision */
+ case YANG_REVISION:
+ if (mod_stmt > Y_MOD_REVISION) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INORD, kw2str(kw), kw2str(prev_kw));
+ return LY_EVALID;
+ }
+ mod_stmt = Y_MOD_REVISION;
+ break;
+
+ /* body */
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ case YANG_AUGMENT:
+ case YANG_CHOICE:
+ case YANG_CONTAINER:
+ case YANG_DEVIATION:
+ case YANG_EXTENSION:
+ case YANG_FEATURE:
+ case YANG_GROUPING:
+ case YANG_IDENTITY:
+ case YANG_LEAF:
+ case YANG_LEAF_LIST:
+ case YANG_LIST:
+ case YANG_NOTIFICATION:
+ case YANG_RPC:
+ case YANG_TYPEDEF:
+ case YANG_USES:
+ case YANG_CUSTOM:
+ mod_stmt = Y_MOD_BODY;
+ break;
+ default:
+ /* error handled in the next switch */
+ break;
+ }
+ prev_kw = kw;
+
+ switch (kw) {
+ /* module header */
+ case YANG_YANG_VERSION:
+ ret = parse_yangversion(ctx, data, mod);
+ break;
+ case YANG_NAMESPACE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_NAMESPACE, 0, &mod->ns, Y_STR_ARG, &mod->exts);
+ break;
+ case YANG_PREFIX:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &mod->prefix, Y_IDENTIF_ARG, &mod->exts);
+ break;
+ case YANG_BELONGS_TO:
+ ret = parse_belongsto(ctx, data, &mod->belongsto, &mod->prefix, &mod->exts);
+ break;
+
+ /* linkage */
+ case YANG_INCLUDE:
+ ret = parse_include(ctx, data, &mod->includes);
+ break;
+ case YANG_IMPORT:
+ ret = parse_import(ctx, data, &mod->imports);
+ break;
+
+ /* meta */
+ case YANG_ORGANIZATION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ORGANIZATION, 0, &mod->org, Y_STR_ARG, &mod->exts);
+ break;
+ case YANG_CONTACT:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_CONTACT, 0, &mod->contact, Y_STR_ARG, &mod->exts);
+ break;
+ case YANG_DESCRIPTION:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &mod->dsc, Y_STR_ARG, &mod->exts);
+ break;
+ case YANG_REFERENCE:
+ ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &mod->ref, Y_STR_ARG, &mod->exts);
+ break;
+
+ /* revision */
+ case YANG_REVISION:
+ ret = parse_revision(ctx, data, &mod->revs);
+ break;
+
+ /* body */
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ ret = parse_any(ctx, data, kw, &mod->data);
+ break;
+ case YANG_CHOICE:
+ ret = parse_choice(ctx, data, &mod->data);
+ break;
+ case YANG_CONTAINER:
+ ret = parse_container(ctx, data, &mod->data);
+ break;
+ case YANG_LEAF:
+ ret = parse_leaf(ctx, data, &mod->data);
+ break;
+ case YANG_LEAF_LIST:
+ ret = parse_leaflist(ctx, data, &mod->data);
+ break;
+ case YANG_LIST:
+ ret = parse_list(ctx, data, &mod->data);
+ break;
+ case YANG_USES:
+ ret = parse_uses(ctx, data, &mod->data);
+ break;
+
+ case YANG_AUGMENT:
+ ret = parse_augment(ctx, data, &mod->augments);
+ break;
+ case YANG_DEVIATION:
+ ret = parse_deviation(ctx, data, &mod->deviations);
+ break;
+ case YANG_EXTENSION:
+ ret = parse_extension(ctx, data, &mod->extensions);
+ break;
+ case YANG_FEATURE:
+ ret = parse_feature(ctx, data, &mod->features);
+ break;
+ case YANG_GROUPING:
+ ret = parse_grouping(ctx, data, &mod->groupings);
+ break;
+ case YANG_IDENTITY:
+ ret = parse_identity(ctx, data, &mod->identities);
+ break;
+ case YANG_NOTIFICATION:
+ ret = parse_notif(ctx, data, &mod->notifs);
+ break;
+ case YANG_RPC:
+ ret = parse_action(ctx, data, &mod->rpcs);
+ break;
+ case YANG_TYPEDEF:
+ ret = parse_typedef(ctx, data, &mod->typedefs);
+ break;
+ case YANG_CUSTOM:
+ ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &mod->exts);
+ break;
+
+ default:
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INCHILDSTMT, kw2str(kw), mod->submodule ? "submodule" : "module");
+ return LY_EVALID;
+ }
+ YANG_ERR_RET(ret);
+ }
+ YANG_ERR_RET(ret);
+
+ /* mandatory substatements */
+ if (mod->submodule) {
+ if (!mod->belongsto) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "belongs-to", "submodule");
+ return LY_EVALID;
+ }
+ } else {
+ if (!mod->ns) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "namespace", "module");
+ return LY_EVALID;
+ } else if (!mod->prefix) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_MISSTMT, "prefix", "module");
+ return LY_EVALID;
+ }
+ }
+
+ return ret;
+}
+
+static LY_ERR
+yang_parse(struct ly_ctx *ctx, const char *data, struct lysp_module **mod_p)
+{
+ LY_ERR ret = 0;
+ char *word, *buf;
+ int word_len;
+ const char *data_start;
+ enum yang_keyword kw;
+ struct lysp_module *mod;
+
+ data_start = data;
+
+ /* "module"/"submodule" */
+ ret = get_keyword(ctx, &data, &kw, &word, &word_len);
+ YANG_ERR_GOTO(ret, error);
+
+ if ((kw != YANG_MODULE) && (kw != YANG_SUBMODULE)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".\n",
+ kw2str(kw));
+ goto error;
+ }
+
+ mod = calloc(1, sizeof *mod);
+ LY_CHECK_ERR_GOTO(!mod, LOGMEM(ctx), error);
+ if (kw == YANG_SUBMODULE) {
+ mod->submodule = 1;
+ }
+
+ /* substatements */
+ ret = parse_sub_module(ctx, &data, mod);
+ YANG_ERR_GOTO(ret, error);
+
+ /* read some trailing spaces or new lines */
+ ret = get_string(ctx, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
+ YANG_ERR_GOTO(ret, error);
+
+ if (word) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
+ word_len, word);
+ free(buf);
+ goto error;
+ }
+ assert(!buf);
+
+ *mod_p = mod;
+ return ret;
+
+error:
+ LOGERR(ctx, LY_EINVAL, "Module parsing failed on line %d.", lysp_get_data_line(data_start, data - data_start));
+ /* TODO free module */
+ return ret;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *data = NULL;
+ int fd = -1;
+ struct stat st;
+ struct lysp_module *mod;
+ struct ly_ctx *ctx = NULL;
+
+ if (argc != 2) {
+ fprintf(stderr, "No arguments specified.\n");
+ goto cleanup;
+ }
+
+ ly_verb(LY_LLERR);
+
+ if (ly_ctx_new(NULL, 0, &ctx)) {
+ fprintf(stderr, "ly_ctx_new() failed.\n");
+ goto cleanup;
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "open() failed (%s).\n", strerror(errno));
+ goto cleanup;
+ }
+
+ if (fstat(fd, &st)) {
+ fprintf(stderr, "fstat() failed (%s).\n", strerror(errno));
+ goto cleanup;
+ }
+
+ data = malloc(st.st_size + 1);
+ if (read(fd, data, st.st_size) < st.st_size) {
+ fprintf(stderr, "read() failed.\n");
+ goto cleanup;
+ }
+ data[st.st_size] = '\0';
+
+ yang_parse(ctx, data, &mod);
+
+cleanup:
+ if (ctx) {
+ ly_ctx_destroy(ctx, NULL);
+ }
+ /* free mod */
+ close(fd);
+ free(data);
+ return 0;
+}