schema tree CHANGE reusing of latest revision schemas

It can be disabled with a CMake option.
diff --git a/src/context.c b/src/context.c
index 61cbba4..8a69e43 100644
--- a/src/context.c
+++ b/src/context.c
@@ -673,8 +673,8 @@
 ly_ctx_load_sub_module(struct ly_ctx *ctx, struct lys_module *module, const char *name, const char *revision,
                        int implement, struct unres_schema *unres)
 {
-    const struct lys_module *mod;
-    char *module_data;
+    struct lys_module *mod;
+    char *module_data = NULL;
     int i;
     void (*module_data_free)(void *module_data) = NULL;
     LYS_INFORMAT format = LYS_IN_UNKNOWN;
@@ -689,52 +689,54 @@
                 }
             }
         }
-        if (revision) {
-            /* try to get the schema with the specific revision from the context,
-             * include the disabled modules in the search to avoid their duplication,
-             * they are enabled by the subsequent call to lys_set_implemented() */
-            for (i = ctx->internal_module_count, mod = NULL; i < ctx->models.used; i++) {
-                mod = ctx->models.list[i]; /* shortcut */
-                if (ly_strequal(name, mod->name, 0) && mod->rev_size && !strcmp(revision, mod->rev[0].date)) {
+        /* try to get the schema from the context (with or without revision),
+         * include the disabled modules in the search to avoid their duplication,
+         * they are enabled by the subsequent call to lys_set_implemented() */
+        for (i = ctx->internal_module_count, mod = NULL; i < ctx->models.used; i++) {
+            mod = ctx->models.list[i]; /* shortcut */
+            if (ly_strequal(name, mod->name, 0)) {
+                if (revision && mod->rev_size && !strcmp(revision, mod->rev[0].date)) {
+                    /* the specific revision was already loaded */
+                    break;
+                } else if (!revision && mod->latest_revision) {
+                    /* the latest revision of this module was already loaded */
                     break;
                 }
+            }
+            mod = NULL;
+        }
+        if (mod) {
+            /* module must be enabled */
+            if (mod->disabled) {
+                lys_set_enabled(mod);
+            }
+            /* module is supposed to be implemented */
+            if (implement && lys_set_implemented(mod)) {
+                /* the schema cannot be implemented */
                 mod = NULL;
             }
-            if (mod) {
-                /* we get such a module, make it implemented */
-                if (lys_set_implemented(mod)) {
-                    /* the schema cannot be implemented */
-                    mod = NULL;
-                }
-                return mod;
-            }
+            return mod;
         }
     }
 
+    /* module is not yet in context, use the user callback or try to find the schema on our own */
     if (ctx->imp_clb) {
+        ly_errno = LY_SUCCESS;
         if (module) {
             mod = lys_main_module(module);
             module_data = ctx->imp_clb(mod->name, (mod->rev_size ? mod->rev[0].date : NULL), name, revision, ctx->imp_clb_data, &format, &module_data_free);
         } else {
             module_data = ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data, &format, &module_data_free);
         }
-        if (!module_data) {
-            if (module || revision) {
-                /* we already know that the specified revision is not present in context, and we have no other
-                 * option in case of submodules */
-                LOGERR(LY_ESYS, "User module retrieval callback failed!");
-                return NULL;
-            } else {
-                /* get the newest revision from the context */
-                mod = ly_ctx_get_module_by(ctx, name, offsetof(struct lys_module, name), revision, 1, 0);
-                if (mod && mod->disabled) {
-                    /* enable the required module */
-                    lys_set_enabled(mod);
-                }
-                return mod;
-            }
+        if (!module_data && (ly_errno != LY_SUCCESS)) {
+            /* callback encountered an error, do not change it */
+            LOGERR(LY_SUCCESS, "User module retrieval callback failed!");
+            return NULL;
         }
+    }
 
+    if (module_data) {
+        /* we got the module from the callback */
         if (module) {
             mod = (struct lys_module *)lys_sub_parse_mem(module, module_data, format, unres);
         } else {
@@ -745,9 +747,17 @@
             module_data_free(module_data);
         }
     } else {
+        /* module was not received from the callback or there is no callback set */
         mod = lyp_search_file(ctx, module, name, revision, implement, unres);
     }
 
+#ifdef LY_ENABLED_LATEST_REVISIONS
+    if (!revision && mod) {
+        /* module is the latest revision found */
+        mod->latest_revision = 1;
+    }
+#endif
+
     return mod;
 }
 
diff --git a/src/libyang.h.in b/src/libyang.h.in
index a8c4234..2ba604a 100644
--- a/src/libyang.h.in
+++ b/src/libyang.h.in
@@ -19,6 +19,8 @@
 
 @ENABLE_CACHE_MACRO@
 
+@ENABLE_LATEST_REVISIONS_MACRO@
+
 #include "tree_schema.h"
 #include "tree_data.h"
 #include "xml.h"
@@ -101,10 +103,10 @@
  * where libyang will automatically search for schemas being imported or included. The search path
  * can be later changed via ly_ctx_set_searchdir() and ly_ctx_unset_searchdrs() functions. If the search dir
  * is specified, it is explored first. Except the searchpath, also all its subdirectories (and symlinks) are
- * taken into account. In case the module is not found, libyang tries to find the (sub)module also in current
+ * taken into account. In case the module is not found, libyang tries to find the (sub)module also in the current
  * working working directory. Note, that in this case only the current directory without any other subdirectory
  * is examined. This automatic searching can be completely avoided when the caller sets module searching callback
- * (#ly_module_imp_clb) via ly_ctx_set_module_imp_clb().
+ * (#ly_module_imp_clb) via ly_ctx_set_module_imp_clb(), but both approaches can also be combined.
  *
  * Schemas are added into the context using [parser functions](@ref howtoschemasparsers) - \b lys_parse_*().
  * In case of schemas, also ly_ctx_load_module() can be used - in that case the #ly_module_imp_clb or automatic
@@ -122,11 +124,18 @@
  * a module (and no other) as target of leafref, augment or deviation. All modules with deviation definition are always
  * marked as implemented. The imported (not implemented) module can be set implemented by lys_set_implemented(). But
  * the implemented module cannot be changed back to just imported module. The imported modules are used only as a
- * source of definitions for types (including identities) and uses statements. The data in such a modules are
- * ignored - caller is not allowed to create the data defined in the model via data parsers, the default nodes are
- * not added into any data tree and mandatory nodes are not checked in the data trees. This can be changed by
- * ly_ctx_set_allimplemented() function which causes that all the imported modules are automatically set to be
- * implemented.
+ * source of definitions for types and groupings for uses statements. The data in such modules are ignored - caller
+ * is not allowed to create the data (including instantiating identities) defined in the model via data parsers,
+ * the default nodes are not added into any data tree and mandatory nodes are not checked in the data trees. This
+ * can be changed by ly_ctx_set_allimplemented() function, which causes that all the imported modules are automatically
+ * set to be implemented.
+ *
+ * When loading/importing a module without revision, the latest revision of the required module is supposed to load.
+ * For a context, the first time the latest revision of a module is requested, it is properly searched for and loaded.
+ * However, when this module is requested (without revision) the second time, the one found previously is returned.
+ * This has the advantage of not searching for the module repeatedly but the drawback that if a later revision
+ * of the module is later made available, this context will not use it. If you are aware of a case when this
+ * optimization could cause problems, you can disable it using a cmake(1) build option (variable).
  *
  * Context holds all modules and their submodules internally. To get
  * a specific module or submodule, use ly_ctx_get_module() and ly_ctx_get_submodule(). There are some additional
@@ -1291,7 +1300,9 @@
  * @param[in] user_data User-supplied callback data.
  * @param[out] format Format of the returned module data.
  * @param[out] free_module_data Callback for freeing the returned module data. If not set, the data will be left untouched.
- * @return Requested module data or NULL if the callback is not able to provide the requested schema content for any reason.
+ * @return Requested module data, NULL if the module is supposed to be loaded
+ * using standard mechanisms (searched for in the filesystem), NULL and #ly_errno set if
+ * the callback failed resulting in the module failing to load.
  */
 typedef char *(*ly_module_imp_clb)(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
                                    void *user_data, LYS_INFORMAT *format, void (**free_module_data)(void *model_data));
@@ -1691,13 +1702,13 @@
  * @ingroup logger
  */
 typedef enum {
-    LY_SUCCESS,    /**< no error, not set by functions, included just to complete #LY_ERR enumeration */
-    LY_EMEM,       /**< Memory allocation failure */
-    LY_ESYS,       /**< System call failure */
-    LY_EINVAL,     /**< Invalid value */
-    LY_EINT,       /**< Internal error */
-    LY_EVALID,     /**< Validation failure */
-    LY_EEXT        /**< Extension error reported by an extension plugin */
+    LY_SUCCESS = 0, /**< no error, not set by functions, included just to complete #LY_ERR enumeration */
+    LY_EMEM,        /**< Memory allocation failure */
+    LY_ESYS,        /**< System call failure */
+    LY_EINVAL,      /**< Invalid value */
+    LY_EINT,        /**< Internal error */
+    LY_EVALID,      /**< Validation failure */
+    LY_EEXT         /**< Extension error reported by an extension plugin */
 } LY_ERR;
 
 /**
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 5ec9b6f..bf8d816 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -619,6 +619,10 @@
                                           - 2 = deviation applied to this module are temporarily off */
     uint8_t disabled:1;              /**< flag if the module is disabled in the context */
     uint8_t implemented:1;           /**< flag if the module is implemented, not just imported */
+    uint8_t latest_revision:1;       /**< flag if the module was loaded without specific revision and is
+                                          the latest revision found */
+    uint8_t padding1:7;              /**< padding for 32b alignment */
+    uint8_t padding2[2];
 
     /* array sizes */
     uint8_t rev_size;                /**< number of elements in #rev array */
@@ -629,8 +633,6 @@
     uint16_t tpdf_size;              /**< number of elements in #tpdf array */
 
     uint8_t features_size;           /**< number of elements in #features array */
-    uint8_t padding[3];              /**< padding for 32b alignment */
-
     uint8_t augment_size;            /**< number of elements in #augment array */
     uint8_t deviation_size;          /**< number of elements in #deviation array */
     uint8_t extensions_size;         /**< number of elements in #extensions array */
@@ -681,6 +683,7 @@
                                           - 2 = deviation applied to this module are temporarily off */
     uint8_t disabled:1;              /**< flag if the module is disabled in the context (same as in main module) */
     uint8_t implemented:1;           /**< flag if the module is implemented, not just imported (same as in main module) */
+    uint8_t padding[3];              /**< padding for 32b alignment */
 
     /* array sizes */
     uint8_t rev_size;                /**< number of elements in #rev array */
@@ -691,8 +694,6 @@
     uint16_t tpdf_size;              /**< number of elements in #tpdf array */
 
     uint8_t features_size;           /**< number of elements in #features array */
-    uint8_t padding[3];              /**< padding for 32b alignment */
-
     uint8_t augment_size;            /**< number of elements in #augment array */
     uint8_t deviation_size;          /**< number of elements in #deviation array */
     uint8_t extensions_size;         /**< number of elements in #extensions array */