schemas FEATURE add schema getters from the context

Also check for the duplicities in the context when parsing a new schema.
diff --git a/src/common.c b/src/common.c
index ab3b71f..dab8288 100644
--- a/src/common.c
+++ b/src/common.c
@@ -11,8 +11,8 @@
  *
  *     https://opensource.org/licenses/BSD-3-Clause
  */
-#define _XOPEN_SOURCE
-#define _DEFAULT_SOURCE
+
+#include "common.h"
 
 #include <assert.h>
 #include <errno.h>
@@ -25,7 +25,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "common.h"
 #include "tree_schema.h"
 
 const char *const ly_stmt_list[] = {
diff --git a/src/common.h b/src/common.h
index d7d17de..afd4d0b 100644
--- a/src/common.h
+++ b/src/common.h
@@ -15,6 +15,10 @@
 #ifndef LY_COMMON_H_
 #define LY_COMMON_H_
 
+#define _DEFAULT_SOURCE
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE
+
 #include <assert.h>
 #include <pthread.h>
 #include <stdint.h>
diff --git a/src/context.c b/src/context.c
index 1216704..ff6dc84 100644
--- a/src/context.c
+++ b/src/context.c
@@ -12,7 +12,8 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _DEFAULT_SOURCE
+#include "common.h"
+
 #define _BSD_SOURCE
 #include <errno.h>
 #include <limits.h>
@@ -23,7 +24,6 @@
 #include <unistd.h>
 
 #include "context.h"
-#include "common.h"
 #include "tree_schema_internal.h"
 #include "libyang.h"
 
@@ -261,6 +261,187 @@
     return ctx->module_set_id;
 }
 
+/**
+ * @brief Iterate over the modules in the given context. Returned modules must match the given key at the offset of
+ * lysp_module and lysc_module structures (they are supposed to be placed at the same offset in both structures).
+ *
+ * @param[in] ctx Context where to iterate.
+ * @param[in] key Key value to search for.
+ * @param[in] key_offset Key's offset in struct lysp_module and struct lysc_module to get value from the context's
+ * modules to match with the key.
+ * @param[in,out] Iterator to pass between the function calls. On the first call, the variable is supposed to be
+ * initiated to 0. After each call returning a module, the value is greater by 1 than the index of the returned
+ * module in the context.
+ * @return Module matching the given key, NULL if no such module found.
+ */
+static const struct lys_module *
+ly_ctx_get_module_by_iter(const struct ly_ctx *ctx, const char *key, size_t key_offset, unsigned int *index)
+{
+    const struct lys_module *mod;
+    const char *value;
+
+    for (; *index < ctx->list.count; ++(*index)) {
+        mod = ctx->list.objs[*index];
+        if (mod->compiled) {
+            value = *(const char**)(((int8_t*)(mod->compiled)) + key_offset);
+        } else {
+            value = *(const char**)(((int8_t*)(mod->parsed)) + key_offset);
+        }
+        if (!strcmp(key, value)) {
+            /* increment index for the next run */
+            ++(*index);
+            return mod;
+        }
+    }
+    /* done */
+    return NULL;
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module() and ly_ctx_get_module_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @param[in] revision Revision date to match. If NULL, the matching module must have no revision. To search for the latest
+ * revision module, use ly_ctx_get_module_latest_by().
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_by(const struct ly_ctx *ctx, const char *key, size_t key_offset, const char *revision)
+{
+    const struct lys_module *mod;
+    unsigned int index = 0;
+
+    while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+        if (!revision) {
+            if ((mod->compiled && !mod->compiled->revs) || (!mod->compiled && !mod->parsed->revs)) {
+                /* found requested module without revision */
+                return mod;
+            }
+        } else {
+            if ((mod->compiled && mod->compiled->revs && !strcmp(mod->compiled->revs[0].date, revision)) ||
+                    (!mod->compiled && mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, revision))) {
+                /* found requested module of the specific revision */
+                return mod;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+    return ly_ctx_get_module_by(ctx, ns, offsetof(struct lysp_module, ns), revision);
+}
+
+API const struct lys_module *
+ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+    return ly_ctx_get_module_by(ctx, name, offsetof(struct lysp_module, name), revision);
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module_latest() and ly_ctx_get_module_latest_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_latest_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
+{
+    const struct lys_module *mod, *newest = NULL;
+    unsigned int index = 0;
+    const char *date, *date_newest = NULL;
+
+    while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+        if (!newest) {
+            newest = mod;
+        } else {
+            if ((newest->compiled && !newest->compiled->revs) || (!newest->compiled && !newest->parsed->revs)) {
+                /* prefer modules with revisions, module with no revision
+                 * is supposed to be the oldest one */
+                newest = mod;
+                date_newest = NULL;
+            } else {
+                if (!date_newest) {
+                    if (newest->compiled) {
+                        date_newest = newest->compiled->revs[0].date;
+                    } else {
+                        date_newest = newest->parsed->revs[0].date;
+                    }
+                }
+                if (mod->compiled) {
+                    date = mod->compiled->revs[0].date;
+                } else {
+                    date = mod->parsed->revs[0].date;
+                }
+                if (strcmp(date, date_newest) > 0) {
+                    /* the current module is newer than so far newest, so remember it */
+                    newest = mod;
+                    date_newest = NULL;
+                }
+            }
+        }
+    }
+
+    return newest;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+    return ly_ctx_get_module_latest_by(ctx, name, offsetof(struct lysp_module, name));
+}
+
+const struct lys_module *
+ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+    return ly_ctx_get_module_latest_by(ctx, ns, offsetof(struct lysp_module, ns));
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module_implemented() and ly_ctx_get_module_implemented_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_implemented_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
+{
+    const struct lys_module *mod;
+    unsigned int index = 0;
+
+    while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+        if ((mod->compiled && mod->compiled->implemented) || (!mod->compiled && mod->parsed->implemented)) {
+            return mod;
+        }
+    }
+
+    return NULL;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+    return ly_ctx_get_module_implemented_by(ctx, name, offsetof(struct lysp_module, name));
+}
+
+API const struct lys_module *
+ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+    return ly_ctx_get_module_implemented_by(ctx, ns, offsetof(struct lysp_module, ns));
+}
+
 API void
 ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv))
 {
diff --git a/src/context.h b/src/context.h
index a172417..2333bb3 100644
--- a/src/context.h
+++ b/src/context.h
@@ -152,6 +152,72 @@
 uint16_t ly_ctx_get_module_set_id(const struct ly_ctx *ctx);
 
 /**
+ * @brief Get YANG module of the given name and revision.
+ *
+ * @param[in] ctx Context to work in.
+ * @param[in] name Name of the YANG module to get.
+ * @param[in] revision Requested revision date of the YANG module to get. If not specified,
+ * the schema with no revision is returned, if it is present in the context.
+ * @return Pointer to the YANG module, NULL if no schema in the context follows the name and revision requirements.
+ */
+const struct lys_module *ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision);
+
+/**
+ * @brief Get the latest revision of the YANG module specified by its name.
+ *
+ * YANG modules with no revision are supposed to be the oldest one.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
+ * given name is present in the context.
+ */
+const struct lys_module *ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name);
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its name.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The only implemented YANG module revision of the given name in the given context. NULL if there is no
+ * implemented module of the given name.
+ */
+const struct lys_module *ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name);
+
+/**
+ * @brief Get YANG module of the given namespace and revision.
+ *
+ * @param[in] ctx Context to work in.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @param[in] revision Requested revision date of the YANG module to get. If not specified,
+ * the schema with no revision is returned, if it is present in the context.
+ * @return Pointer to the YANG module, NULL if no schema in the context follows the namespace and revision requirements.
+ */
+const struct lys_module *ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision);
+
+/**
+ * @brief Get the latest revision of the YANG module specified by its namespace.
+ *
+ * YANG modules with no revision are supposed to be the oldest one.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
+ * given namespace is present in the context.
+ */
+const struct lys_module *ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns);
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its namespace.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @return The only implemented YANG module revision of the given namespace in the given context. NULL if there is no
+ * implemented module of the given namespace.
+ */
+const struct lys_module *ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns);
+
+/**
  * @brief Free all internal structures of the specified context.
  *
  * The function should be used before terminating the application to destroy
diff --git a/src/log.c b/src/log.c
index 18d0b38..57da747 100644
--- a/src/log.c
+++ b/src/log.c
@@ -12,14 +12,14 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
+#include "common.h"
+
 #include <assert.h>
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "libyang.h"
-#include "common.h"
 #include "context.h"
 
 THREAD_LOCAL enum int_log_opts log_opt;
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 434073d..7ea75c6 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -1424,7 +1424,7 @@
         return LY_EVALID;
     }
 
-    strncpy(rev->rev, word, word_len);
+    strncpy(rev->date, word, word_len);
     free(buf);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 3960bb3..60bd9b9 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -11,7 +11,8 @@
  *
  *     https://opensource.org/licenses/BSD-3-Clause
  */
-#define _DEFAULT_SOURCE
+
+#include "common.h"
 
 #include <ctype.h>
 #include <errno.h>
@@ -24,7 +25,6 @@
 #include <unistd.h>
 
 #include "libyang.h"
-#include "common.h"
 #include "context.h"
 #include "tree_schema_internal.h"
 
@@ -1076,7 +1076,7 @@
     return ret;
 }
 
-const struct lys_module *
+static const struct lys_module *
 lys_parse_mem_(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, const char *revision, int implement)
 {
     struct lys_module *mod = NULL;
@@ -1103,21 +1103,36 @@
     }
 
     if (implement) {
+        if (ly_ctx_get_module_implemented(ctx, mod->parsed->name)) {
+            LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->parsed->name);
+            lys_module_free(mod, NULL);
+            return NULL;
+        }
         mod->parsed->implemented = 1;
     }
 
     if (revision) {
         /* check revision of the parsed model */
-        if (!mod->parsed->revs || strcmp(revision, mod->parsed->revs[0].rev)) {
+        if (!mod->parsed->revs || strcmp(revision, mod->parsed->revs[0].date)) {
             LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").",
-                   mod->parsed->name, mod->parsed->revs[0].rev, revision);
-            lysp_module_free(mod->parsed);
-            free(mod);
+                   mod->parsed->name, mod->parsed->revs[0].date, revision);
+            lys_module_free(mod, NULL);
             return NULL;
         }
     }
 
     /* check for duplicity in the context */
+    if (ly_ctx_get_module(ctx, mod->parsed->name, mod->parsed->revs ? mod->parsed->revs[0].date : NULL)) {
+        if (mod->parsed->revs) {
+            LOGERR(ctx, LY_EEXIST, "Module \"%s\" of revision \"%s\" is already present in the context.",
+                   mod->parsed->name, mod->parsed->revs[0].date);
+        } else {
+            LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
+                   mod->parsed->name);
+        }
+        lys_module_free(mod, NULL);
+        return NULL;
+    }
 
     /* add into context */
     ly_set_add(&ctx->list, mod, LY_SET_OPT_USEASLIST);
@@ -1238,9 +1253,9 @@
     }
     if (rev) {
         len = dot - ++rev;
-        if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].rev, rev, len)) {
+        if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].date, rev, len)) {
             LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
-                   mod->parsed->revs ? mod->parsed->revs[0].rev : "none");
+                   mod->parsed->revs ? mod->parsed->revs[0].date : "none");
         }
     }
 
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 12394eb..3b939ea 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -308,7 +308,7 @@
  * @brief YANG revision-stmt
  */
 struct lysp_revision {
-    char rev[LY_REV_SIZE];           /**< revision date (madatory) */
+    char date[LY_REV_SIZE];           /**< revision date (madatory) */
     const char *dsc;                 /**< description statement */
     const char *ref;                 /**< reference statement */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
@@ -739,7 +739,6 @@
 struct lysp_module {
     struct ly_ctx *ctx;              /**< libyang context of the module (mandatory) */
     const char *name;                /**< name of the module (mandatory) */
-    const char *filepath;            /**< path, if the schema was read from a file, NULL in case of reading from memory */
     union {
         /* module */
         const char *ns;              /**< namespace of the module (module - mandatory) */
@@ -747,14 +746,15 @@
         const char *belongsto;       /**< belongs to parent module (submodule - mandatory) */
     };
     const char *prefix;              /**< module prefix or submodule belongsto prefix of main module (mandatory) */
+    struct lysp_revision *revs;      /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
+                                          in the list is always the last (newest) revision of the module */
     struct lysp_import *imports;     /**< list of imported modules ([sized array](@ref sizedarrays)) */
     struct lysp_include *includes;   /**< list of included submodules ([sized array](@ref sizedarrays)) */
+    const char *filepath;            /**< path, if the schema was read from a file, NULL in case of reading from memory */
     const char *org;                 /**< party/company responsible for the module */
     const char *contact;             /**< contact information for the module */
     const char *dsc;                 /**< description of the module */
     const char *ref;                 /**< cross-reference for the module */
-    struct lysp_revision *revs;      /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
-                                          in the list is always the last (newest) revision of the module */
     struct lysp_ext *extensions;     /**< list of extension statements ([sized array](@ref sizedarrays)) */
     struct lysp_feature *features;   /**< list of feature definitions ([sized array](@ref sizedarrays)) */
     struct lysp_ident *identities;   /**< list of identities ([sized array](@ref sizedarrays)) */
@@ -825,6 +825,14 @@
  */
 
 /**
+ * @brief Compiled YANG revision statement
+ */
+struct lysc_revision {
+    char date[LY_REV_SIZE];          /**< revision-date (mandatory) */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+/**
  * @brief Compiled YANG if-feature-stmt
  */
 struct lysc_iffeature {
@@ -857,6 +865,8 @@
     const char *name;                /**< name of the module (mandatory) */
     const char *ns;                  /**< namespace of the module (mandatory) */
     const char *prefix;              /**< module prefix (mandatory) */
+    struct lysc_revision *revs;      /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
+                                          in the list is always the last (newest) revision of the module */
     struct lysc_import *imports;     /**< list of imported modules ([sized array](@ref sizedarrays)) */
 
 
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 0d0620c..b95505a 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -92,7 +92,7 @@
     struct lysp_revision rev;
 
     for (i = 1, r = 0; revs && i < LY_ARRAY_SIZE(revs); i++) {
-        if (strcmp(revs[i].rev, revs[r].rev) > 0) {
+        if (strcmp(revs[i].date, revs[r].date) > 0) {
             r = i;
         }
     }