extensions FEATURE yang-data extension implementation

Internal libyang support for yang-data extension defined in
ietf-restconf module (RFC 8040)

Fixes #1334
diff --git a/src/plugins_exts_yangdata.c b/src/plugins_exts_yangdata.c
new file mode 100644
index 0000000..148360b
--- /dev/null
+++ b/src/plugins_exts_yangdata.c
@@ -0,0 +1,163 @@
+/**
+ * @file plugins_exts_yangdata.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang extension plugin - yang-data (RFC 8040)
+ *
+ * Copyright (c) 2021 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 <stdlib.h>
+
+#include "common.h"
+#include "plugins_exts.h"
+#include "schema_compile.h"
+#include "tree_schema.h"
+
+/**
+ * @brief Storage for ID used to check plugin API version compatibility.
+ * Ignored here in the internal plugin.
+LYEXT_VERSION_CHECK
+ */
+
+/**
+ * @brief Free yang-data extension instances' data.
+ *
+ * Implementation of ::lyext_clb_free callback set as lyext_plugin::free.
+ */
+void
+yangdata_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext)
+{
+    lysc_extension_instance_substatements_free(ctx, ext->substmts);
+}
+
+/**
+ * @brief Compile yang-data extension instances.
+ *
+ * Implementation of lyext_clb_compile callback set as lyext_plugin::compile.
+ */
+LY_ERR
+yangdata_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext)
+{
+    LY_ERR ret;
+    LY_ARRAY_COUNT_TYPE u;
+    struct lysc_module *mod_c;
+    const struct lysc_node *child;
+    ly_bool valid = 1;
+    uint32_t prev_options = cctx->options;
+
+    /* yang-data can appear only at the top level of a YANG module or submodule */
+    if (c_ext->parent_type != LYEXT_PAR_MODULE) {
+        lyext_log(c_ext, LY_LLWRN, 0, cctx->path,
+                "Extension %s is ignored since it appears as a non top-level statement in \"%s\" statement.",
+                p_ext->name, lyext_parent2str(c_ext->parent_type));
+        return LY_ENOT;
+    }
+    /* check mandatory argument */
+    if (!c_ext->argument) {
+        lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path,
+                "Extension %s is instantiated without mandatory argument representing YANG data template name.",
+                p_ext->name);
+        return LY_EVALID;
+    }
+
+    mod_c = (struct lysc_module *)c_ext->parent;
+
+    /* check for duplication */
+    LY_ARRAY_FOR(mod_c->exts, u) {
+        if ((&mod_c->exts[u] != c_ext) && (mod_c->exts[u].def == c_ext->def) && !strcmp(mod_c->exts[u].argument, c_ext->argument)) {
+            /* duplication of the same yang-data extension in a single module */
+            lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path, "Extension %s is instantiated multiple times.", p_ext->name);
+            return LY_EVALID;
+        }
+    }
+
+    /* compile annotation substatements
+     * To let the compilation accept different statements possibly leading to the container top-level node, there are 3
+     * allowed substatements pointing to a single storage. But when compiled, the substaments list is compressed just to
+     * a single item providing the schema tree. */
+    LY_ARRAY_CREATE_RET(cctx->ctx, c_ext->substmts, 3, LY_EMEM);
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[0].stmt = LY_STMT_CONTAINER;
+    c_ext->substmts[0].cardinality = LY_STMT_CARD_OPT;
+    c_ext->substmts[0].storage = &c_ext->data;
+
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[1].stmt = LY_STMT_CHOICE;
+    c_ext->substmts[1].cardinality = LY_STMT_CARD_OPT;
+    c_ext->substmts[1].storage = &c_ext->data;
+
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[2].stmt = LY_STMT_USES;
+    c_ext->substmts[2].cardinality = LY_STMT_CARD_OPT;
+    c_ext->substmts[2].storage = &c_ext->data;
+
+    cctx->options |= LYS_COMPILE_NO_CONFIG | LYS_COMPILE_NO_DISABLED;
+    ret = lys_compile_extension_instance(cctx, p_ext, c_ext);
+    cctx->options = prev_options;
+    LY_ARRAY_DECREMENT(c_ext->substmts);
+    LY_ARRAY_DECREMENT(c_ext->substmts);
+    LY_CHECK_RET(ret);
+
+    /* check that we have really just a single container data definition in the top */
+    child = *(struct lysc_node **)c_ext->substmts[0].storage;
+    if (!child) {
+        valid = 0;
+        lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path,
+                "Extension %s is instantiated without any top level data node, but exactly one container data node is expected.",
+                p_ext->name);
+    } else if (child->next) {
+        valid = 0;
+        lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path,
+                "Extension %s is instantiated with multiple top level data nodes, but only a single container data node is allowed.",
+                p_ext->name);
+    } else if (child->nodetype == LYS_CHOICE) {
+        /* all the choice's case are expected to result to a single container node */
+        const struct lysc_node *snode = NULL;
+
+        while ((snode = lys_getnext(snode, child, mod_c, 0))) {
+            if (snode->next) {
+                valid = 0;
+                lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path,
+                        "Extension %s is instantiated with multiple top level data nodes (inside a single choice's case), "
+                        "but only a single container data node is allowed.", p_ext->name);
+                break;
+            } else if (snode->nodetype != LYS_CONTAINER) {
+                valid = 0;
+                lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path,
+                        "Extension %s is instantiated with %s top level data node (inside a choice), "
+                        "but only a single container data node is allowed.", p_ext->name, lys_nodetype2str(snode->nodetype));
+                break;
+            }
+        }
+    } else if (child->nodetype != LYS_CONTAINER) {
+        /* via uses */
+        valid = 0;
+        lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path,
+                "Extension %s is instantiated with %s top level data node, but only a single container data node is allowed.",
+                p_ext->name, lys_nodetype2str(child->nodetype));
+    }
+
+    if (!valid) {
+        yangdata_free(cctx->ctx, c_ext);
+        c_ext->data = c_ext->substmts = NULL;
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Plugin for the yang-data extension
+ */
+struct lyext_plugin yangdata_plugin = {
+    .id = "libyang 2 - yang-data, version 1",
+    .compile = &yangdata_compile,
+    .validate = NULL,
+    .free = yangdata_free
+};