parsers CHANGE redesign API of all the parsers

Simplify work with different inputs by introducing ly_in handler similar
to the printers' ly_out. The input handler can be used repeatedly and
also some input data manipulation functions are added.

Add new lys_parse() as a generic function using ly_in input handler. The
current API (lys_parse_* functions) was rewritten to be a wrapper of
the generic lys_parse().

Next to the unit tests for the parsers functions, also the similar tests
for printers functions are added.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c3d1f1b..9db7241 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -222,6 +222,7 @@
     src/tree_schema_free.c
     src/tree_schema_compile.c
     src/tree_schema_helpers.c
+    src/parser.c
     src/parser_yang.c
     src/parser_yin.c
     src/parser_stmt.c
@@ -251,6 +252,10 @@
     src/context.h
     src/tree.h
     src/tree_data.h
+    src/parser.h
+    src/parser_schema.h
+    src/plugins_exts.h
+    src/plugins_types.h
     src/printer.h
     src/printer_data.h
     src/tree_schema.h
diff --git a/src/context.c b/src/context.c
index 338c824..9da44c5 100644
--- a/src/context.c
+++ b/src/context.c
@@ -251,7 +251,7 @@
         module = (struct lys_module *)lys_parse_mem_module(ctx, internal_modules[i].data, internal_modules[i].format,
                                                            internal_modules[i].implemented, NULL, NULL);
         LY_CHECK_ERR_GOTO(!module, rc = ly_errcode(ctx), error);
-        LY_CHECK_GOTO((rc = lys_compile(module, 0)), error);
+        LY_CHECK_GOTO((rc = lys_compile(&module, 0)), error);
     }
 
     *new_ctx = ctx;
diff --git a/src/context.h b/src/context.h
index a1b771b..7dabc3e 100644
--- a/src/context.h
+++ b/src/context.h
@@ -18,6 +18,7 @@
 #include <stdint.h>
 
 #include "log.h"
+#include "parser_schema.h"
 #include "tree_schema.h"
 
 #ifdef __cplusplus
diff --git a/src/libyang.h b/src/libyang.h
index c86bd13..72486e3 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -22,6 +22,12 @@
 #endif
 
 #include "log.h"
+#include "parser.h"
+#include "parser_schema.h"
+#include "plugins_types.h"
+#include "printer.h"
+#include "printer_data.h"
+#include "printer_schema.h"
 #include "set.h"
 #include "dict.h"
 #include "context.h"
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..0b2761d
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,314 @@
+/**
+ * @file printer.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic libyang printers functions.
+ *
+ * Copyright (c) 2015 - 2019 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
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "parser_internal.h"
+
+API LY_IN_TYPE
+ly_in_type(const struct ly_in *in)
+{
+    LY_CHECK_ARG_RET(NULL, in, LY_IN_ERROR);
+    return in->type;
+}
+
+API LY_ERR
+ly_in_new_fd(int fd, struct ly_in **in)
+{
+    size_t length;
+    char *addr;
+
+    LY_CHECK_ARG_RET(NULL, fd >= 0, in, LY_EINVAL);
+
+    LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr));
+    if (!addr) {
+        LOGERR(NULL, LY_EINVAL, "Empty input file.");
+        return LY_EINVAL;
+    }
+
+    *in = calloc(1, sizeof **in);
+    LY_CHECK_ERR_RET(!*in, LOGMEM(NULL); ly_munmap(addr, length), LY_EMEM);
+
+    (*in)->type = LY_IN_FD;
+    (*in)->method.fd = fd;
+    (*in)->current = (*in)->start = addr;
+    (*in)->length = length;
+
+    return LY_SUCCESS;
+}
+
+API int
+ly_in_fd(struct ly_in *in, int fd)
+{
+    int prev_fd;
+    size_t length;
+    const char *addr;
+
+    LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FD, -1);
+
+    prev_fd = in->method.fd;
+
+    if (fd != -1) {
+        LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr), -1);
+        if (!addr) {
+            LOGERR(NULL, LY_EINVAL, "Empty input file.");
+            return -1;
+        }
+
+        ly_munmap((char*)in->start, in->length);
+
+        in->method.fd = fd;
+        in->current = in->start = addr;
+        in->length = length;
+    }
+
+    return prev_fd;
+}
+
+API LY_ERR
+ly_in_new_file(FILE *f, struct ly_in **in)
+{
+    LY_CHECK_ARG_RET(NULL, f, in, LY_EINVAL);
+
+    LY_CHECK_RET(ly_in_new_fd(fileno(f), in));
+
+    /* convert the LY_IN_FD input handler into the LY_IN_FILE */
+    (*in)->type = LY_IN_FILE;
+    (*in)->method.f = f;
+
+    return LY_SUCCESS;
+}
+
+API FILE *
+ly_in_file(struct ly_in *in, FILE *f)
+{
+    FILE *prev_f;
+
+    LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILE, NULL);
+
+    prev_f = in->method.f;
+
+    if (f) {
+        /* convert LY_IN_FILE handler into LY_IN_FD to be able to update it via ly_in_fd() */
+        in->type = LY_IN_FD;
+        in->method.fd = fileno(prev_f);
+        if (ly_in_fd(in, fileno(f)) == -1) {
+            in->type = LY_IN_FILE;
+            in->method.f = prev_f;
+            return NULL;
+        }
+
+        /* if success, convert the result back */
+        in->type = LY_IN_FILE;
+        in->method.f = f;
+    }
+
+    return prev_f;
+}
+
+API LY_ERR
+ly_in_new_memory(const char *str, struct ly_in **in)
+{
+    LY_CHECK_ARG_RET(NULL, str, in, LY_EINVAL);
+
+    *in = calloc(1, sizeof **in);
+    LY_CHECK_ERR_RET(!*in, LOGMEM(NULL), LY_EMEM);
+
+    (*in)->type = LY_IN_MEMORY;
+    (*in)->start = (*in)->current = str;
+
+    return LY_SUCCESS;
+}
+
+const char *
+ly_in_memory(struct ly_in *in, const char *str)
+{
+    const char *data;
+
+    LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_MEMORY, NULL);
+
+    data = in->current;
+
+    if (str) {
+        in->start = in->current = str;
+    }
+
+    return data;
+}
+
+API LY_ERR
+ly_in_reset(struct ly_in *in)
+{
+    LY_CHECK_ARG_RET(NULL, in, LY_EINVAL);
+
+    in->current = in->start;
+    return LY_SUCCESS;
+}
+
+API LY_ERR
+ly_in_new_filepath(const char *filepath, size_t len, struct ly_in **in)
+{
+	LY_ERR ret;
+    char *fp;
+    int fd;
+
+    LY_CHECK_ARG_RET(NULL, filepath, in, LY_EINVAL);
+
+    if (len) {
+        fp = strndup(filepath, len);
+    } else {
+        fp = strdup(filepath);
+    }
+
+    fd = open(fp, O_RDONLY);
+    LY_CHECK_ERR_RET(!fd, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp), LY_ESYS);
+
+    LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, in), free(fp), ret);
+
+    /* convert the LY_IN_FD input handler into the LY_IN_FILE */
+    (*in)->type = LY_IN_FILEPATH;
+    (*in)->method.fpath.fd = fd;
+    (*in)->method.fpath.filepath = fp;
+
+    return LY_SUCCESS;
+}
+
+API const char *
+ly_in_filepath(struct ly_in *in, const char *filepath, size_t len)
+{
+    int fd, prev_fd;
+    char *fp = NULL;
+
+    LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILEPATH, filepath ? NULL : ((void *)-1));
+
+    if (!filepath) {
+        return in->method.fpath.filepath;
+    }
+
+    if (len) {
+        fp = strndup(filepath, len);
+    } else {
+        fp = strdup(filepath);
+    }
+
+    /* replace filepath */
+    fd = open(fp, O_RDONLY);
+    LY_CHECK_ERR_RET(!fd, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp), NULL);
+
+    /* convert LY_IN_FILEPATH handler into LY_IN_FD to be able to update it via ly_in_fd() */
+    in->type = LY_IN_FD;
+    prev_fd = ly_in_fd(in, fd);
+    LY_CHECK_ERR_RET(prev_fd == -1, in->type = LY_IN_FILEPATH; free(fp), NULL);
+
+    /* and convert the result back */
+    in->type = LY_IN_FILEPATH;
+    close(prev_fd);
+    free(in->method.fpath.filepath);
+    in->method.fpath.fd = fd;
+    in->method.fpath.filepath = fp;
+
+    return NULL;
+}
+
+void
+lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath)
+{
+    char path[PATH_MAX];
+    char proc_path[32];
+    int len;
+
+    LY_CHECK_ARG_RET(NULL, ctx, in, filepath, );
+    if (*filepath) {
+        /* filepath already set */
+        return;
+    }
+
+    switch (in->type) {
+    case LY_IN_FILEPATH:
+        if (realpath(in->method.fpath.filepath, path) != NULL) {
+            *filepath = lydict_insert(ctx, path, 0);
+        } else {
+            *filepath = lydict_insert(ctx, in->method.fpath.filepath, 0);
+        }
+
+        break;
+    case LY_IN_FD:
+#ifdef __APPLE__
+        if (fcntl(in->method.fd, F_GETPATH, path) != -1) {
+            *filepath = lydict_insert(ctx, path, 0);
+        }
+#else
+        /* get URI if there is /proc */
+        sprintf(proc_path, "/proc/self/fd/%d", in->method.fd);
+        if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
+            *filepath = lydict_insert(ctx, path, len);
+        }
+#endif
+        break;
+    case LY_IN_MEMORY:
+    case LY_IN_FILE:
+        /* nothing to do */
+        break;
+    default:
+        LOGINT(ctx);
+        break;
+    }
+
+}
+
+API void
+ly_in_free(struct ly_in *in, int destroy)
+{
+    if (!in) {
+        return;
+    } else if (in->type == LY_IN_ERROR) {
+        LOGINT(NULL);
+        return;
+    }
+
+    if (destroy) {
+        if (in->type == LY_IN_MEMORY) {
+            free((char*)in->start);
+        } else {
+            ly_munmap((char*)in->start, in->length);
+
+            if (in->type == LY_IN_FILE) {
+                fclose(in->method.f);
+            } else {
+                close(in->method.fd);
+
+                if (in->type == LY_IN_FILEPATH) {
+                    free(in->method.fpath.filepath);
+                }
+            }
+        }
+    } else if (in->type != LY_IN_MEMORY) {
+        ly_munmap((char*)in->start, in->length);
+
+        if (in->type == LY_IN_FILEPATH) {
+            close(in->method.fpath.fd);
+            free(in->method.fpath.filepath);
+        }
+    }
+
+    free(in);
+}
diff --git a/src/parser.h b/src/parser.h
new file mode 100644
index 0000000..fcdc5dc
--- /dev/null
+++ b/src/parser.h
@@ -0,0 +1,158 @@
+/**
+ * @file parser.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic libyang parsers structures and functions
+ *
+ * Copyright (c) 2020 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
+ */
+
+#ifndef LY_PARSER_H_
+#define LY_PARSER_H_
+
+#include <unistd.h>
+
+/**
+ * @brief Parser input structure specifying where the data are read.
+ */
+struct ly_in;
+
+/**
+ * @brief Types of the parser's inputs
+ */
+typedef enum LY_IN_TYPE {
+    LY_IN_ERROR = -1,  /**< error value to indicate failure of the functions returning LY_IN_TYPE */
+    LY_IN_FD,          /**< file descriptor printer */
+    LY_IN_FILE,        /**< FILE stream parser */
+    LY_IN_FILEPATH,    /**< filepath parser */
+    LY_IN_MEMORY       /**< memory parser */
+} LY_IN_TYPE;
+
+/**
+ * @brief Get input type of the input handler.
+ *
+ * @param[in] in Input handler.
+ * @return Type of the parser's input.
+ */
+LY_IN_TYPE ly_in_type(const struct ly_in *in);
+
+/**
+ * @brief Reset the input medium to read from its beginning, so the following parser function will read from the object's beginning.
+ *
+ * Note that in case the underlying output is not seekable (stream referring a pipe/FIFO/socket or the callback output type),
+ * nothing actually happens despite the function succeeds. Also note that the medium is not returned to the state it was when
+ * the handler was created. For example, file is seeked into the offset zero, not to the offset where it was opened when
+ * ly_in_new_file() was called.
+ *
+ * @param[in] in Input handler.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ESYS in case of failure
+ */
+LY_ERR ly_in_reset(struct ly_in *in);
+
+/**
+ * @brief Create input handler using file descriptor.
+ *
+ * @param[in] fd File descriptor to use.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LY_ERR ly_in_new_fd(int fd, struct ly_in **in);
+
+/**
+ * @brief Get or reset file descriptor input handler.
+ *
+ * @param[in] in Input handler.
+ * @param[in] fd Optional value of a new file descriptor for the handler. If -1, only the current file descriptor value is returned.
+ * @return Previous value of the file descriptor. Note that caller is responsible for closing the returned file descriptor in case of setting new descriptor @p fd.
+ * @return -1 in case of error when setting up the new file descriptor.
+ */
+int ly_in_fd(struct ly_in *in, int fd);
+
+/**
+ * @brief Create input handler using file stream.
+ *
+ * @param[in] f File stream to use.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LY_ERR ly_in_new_file(FILE *f, struct ly_in **in);
+
+/**
+ * @brief Get or reset file stream input handler.
+ *
+ * @param[in] in Input handler.
+ * @param[in] f Optional new file stream for the handler. If NULL, only the current file stream is returned.
+ * @return NULL in case of invalid argument or an error when setting up the new input file, original input handler @p in is untouched in this case.
+ * @return Previous file stream of the handler. Note that caller is responsible for closing the returned stream in case of setting new stream @p f.
+ */
+FILE *ly_in_file(struct ly_in *in, FILE *f);
+
+/**
+ * @brief Create input handler using memory to read data.
+ *
+ * @param[in] str Pointer where to start reading data. The input data are expected to be NULL-terminated.
+ * Note that in case the destroy argument of ly_in_free() is used, the input string is passed to free(),
+ * so if it is really a static string, do not use the destroy argument!
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LY_ERR ly_in_new_memory(const char *str, struct ly_in **in);
+
+/**
+ * @brief Get or change memory where the data are read from.
+ *
+ * @param[in] in Input handler.
+ * @param[in] str String containing the data to read. The input data are expected to be NULL-terminated.
+ * Note that in case the destroy argument of ly_in_free() is used, the input string is passed to free(),
+ * so if it is really a static string, do not use the destroy argument!
+ * @return Previous starting address to read data from. Note that the caller is responsible to free
+ * the data in case of changing string pointer @p str.
+ */
+const char *ly_in_memory(struct ly_in *in, const char *str);
+
+/**
+ * @brief Create input handler file of the given filename.
+ *
+ * @param[in] filepath Path of the file where to read data.
+ * @param[in] len Optional number of bytes to use from @p filepath. If 0, the @p filepath is considered to be NULL-terminated and
+ * the whole string is taken into account.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LY_ERR ly_in_new_filepath(const char *filepath, size_t len, struct ly_in **in);
+
+/**
+ * @brief Get or change the filepath of the file where the parser reads the data.
+ *
+ * Note that in case of changing the filepath, the current file is closed and a new one is
+ * created/opened instead of renaming the previous file. Also note that the previous filepath
+ * string is returned only in case of not changing it's value.
+ *
+ * @param[in] in Input handler.
+ * @param[in] filepath Optional new filepath for the handler. If and only if NULL, the current filepath string is returned.
+ * @param[in] len Optional number of bytes to use from @p filepath. If 0, the @p filepath is considered to be NULL-terminated and
+ * the whole string is taken into account.
+ * @return Previous filepath string in case the @p filepath argument is NULL.
+ * @return NULL if changing filepath succeedes and ((void *)-1) otherwise.
+ */
+const char *ly_in_filepath(struct ly_in *in, const char *filepath, size_t len);
+
+/**
+ * @brief Free the input handler.
+ * @param[in] in Input handler to free.
+ * @param[in] destroy Flag to free the input data buffer (for LY_IN_MEMORY) or to
+ * close stream/file descriptor (for LY_IN_FD and LY_IN_FILE)
+ */
+void ly_in_free(struct ly_in *in, int destroy);
+
+#endif /* LY_PARSER_H_ */
diff --git a/src/parser_internal.h b/src/parser_internal.h
new file mode 100644
index 0000000..9dc3c50
--- /dev/null
+++ b/src/parser_internal.h
@@ -0,0 +1,86 @@
+/**
+ * @file parser_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Internal structures and functions for libyang parsers
+ *
+ * Copyright (c) 2020 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
+ */
+
+#ifndef LY_PARSER_INTERNAL_H_
+#define LY_PARSER_INTERNAL_H_
+
+#include "parser.h"
+#include "tree_schema_internal.h"
+
+/**
+ * @brief Parser input structure specifying where the data are read.
+ */
+struct ly_in {
+    LY_IN_TYPE type;     /**< type of the output to select the output method */
+    const char *current;
+    const char *start;
+    size_t length;
+    union {
+        int fd;          /**< file descriptor for LY_IN_FD type */
+        FILE *f;         /**< file structure for LY_IN_FILE and LY_IN_FILEPATH types */
+        struct {
+            int fd;      /**< file descriptor for LY_IN_FILEPATH */
+            char *filepath;   /**< stored original filepath */
+        } fpath;         /**< filepath structure for LY_IN_FILEPATH */
+    } method;            /**< type-specific information about the output */
+};
+
+/**
+ * @brief Parse submodule from YANG data.
+ * @param[in,out] ctx Parser context.
+ * @param[in] ly_ctx Context of YANG schemas.
+ * @param[in] main_ctx Parser context of main module.
+ * @param[in] data Input data to be parsed.
+ * @param[out] submod Pointer to the parsed submodule structure.
+ * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
+ */
+LY_ERR yang_parse_submodule(struct lys_yang_parser_ctx **context, struct ly_ctx *ly_ctx, struct lys_parser_ctx *main_ctx,
+                            const char *data, struct lysp_submodule **submod);
+
+/**
+ * @brief Parse module from YANG data.
+ * @param[in] ctx Parser context.
+ * @param[in] data Input data to be parsed.
+ * @param[in, out] mod Prepared module structure where the parsed information, including the parsed
+ * module structure, will be filled in.
+ * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
+ */
+LY_ERR yang_parse_module(struct lys_yang_parser_ctx **context, const char *data, struct lys_module *mod);
+
+/**
+ * @brief Parse module from YIN data.
+ *
+ * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
+ * @param[in] data Input data to be parsed.
+ * @param[in,out] mod Prepared module structure where the parsed information, including the parsed
+ * module structure, will be filled in.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_module(struct lys_yin_parser_ctx **yin_ctx, const char *data, struct lys_module *mod);
+
+/**
+ * @brief Parse submodule from YIN data.
+ *
+ * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
+ * @param[in] ctx Libyang context.
+ * @param[in] main_ctx Parser context of main module.
+ * @param[in,out] data Input data to be parsed.
+ * @param[in,out] submod Submodule structure where the parsed information, will be filled in.
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_submodule(struct lys_yin_parser_ctx **yin_ctx, struct ly_ctx *ctx, struct lys_parser_ctx *main_ctx,
+                           const char *data, struct lysp_submodule **submod);
+
+#endif /* LY_PARSER_INTERNAL_H_ */
diff --git a/src/parser_schema.h b/src/parser_schema.h
new file mode 100644
index 0000000..82bdd9b
--- /dev/null
+++ b/src/parser_schema.h
@@ -0,0 +1,114 @@
+/**
+ * @file parser_schema.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema parsers for libyang
+ *
+ * Copyright (c) 2015-2020 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
+ */
+
+#ifndef LY_PARSER_SCHEMA_H_
+#define LY_PARSER_SCHEMA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_in;
+
+/**
+ * @addtogroup schematree
+ * @{
+ */
+
+/**
+ * @brief Schema input formats accepted by libyang [parser functions](@ref howtoschemasparsers).
+ */
+typedef enum {
+    LYS_IN_UNKNOWN = 0,  /**< unknown format, used as return value in case of error */
+    LYS_IN_YANG = 1,     /**< YANG schema input format */
+    LYS_IN_YIN = 3       /**< YIN schema input format */
+} LYS_INFORMAT;
+
+/**
+ * @brief Load a schema into the specified context.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] in The input handle to provide the dumped data model in the specified format.
+ * @param[in] format Format of the schema to parse.
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format);
+
+/**
+ * @brief Load a schema into the specified context.
+ *
+ * This function is comsidered for a simple use, if you have a complex usecase,
+ * consider use of lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] data The string containing the dumped data model in the specified
+ * format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format);
+
+/**
+ * @brief Read a schema from file descriptor into the specified context.
+ *
+ * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
+ *
+ * This function is comsidered for a simple use, if you have a complex usecase,
+ * consider use of lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
+ *            in the specified format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format);
+
+/**
+ * @brief Load a schema into the specified context from a file.
+ *
+ * This function is comsidered for a simple use, if you have a complex usecase,
+ * consider use of lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] path Path to the file with the model in the specified format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format);
+
+/**
+ * @brief Search for the schema file in the specified searchpaths.
+ *
+ * @param[in] searchpaths NULL-terminated array of paths to be searched (recursively). Current working
+ * directory is searched automatically (but non-recursively if not in the provided list). Caller can use
+ * result of the ly_ctx_get_searchdirs().
+ * @param[in] cwd Flag to implicitly search also in the current working directory (non-recursively).
+ * @param[in] name Name of the schema to find.
+ * @param[in] revision Revision of the schema to find. If NULL, the newest found schema filepath is returned.
+ * @param[out] localfile Mandatory output variable containing absolute path of the found schema. If no schema
+ * complying the provided restriction is found, NULL is set.
+ * @param[out] format Optional output variable containing expected format of the schema document according to the
+ * file suffix.
+ * @return LY_ERR value (LY_SUCCESS is returned even if the file is not found, then the *localfile is NULL).
+ */
+LY_ERR lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision, char **localfile, LYS_INFORMAT *format);
+
+/** @} schematree */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PARSER_SCHEMA_H_ */
diff --git a/src/printer_schema.h b/src/printer_schema.h
index b06d216..892f326 100644
--- a/src/printer_schema.h
+++ b/src/printer_schema.h
@@ -40,6 +40,18 @@
 /** @} schemaprinterflags */
 
 /**
+ * @brief Schema output formats accepted by libyang [printer functions](@ref howtoschemasprinters).
+ */
+typedef enum {
+    LYS_OUT_UNKNOWN = 0, /**< unknown format, used as return value in case of error */
+    LYS_OUT_YANG = 1,    /**< YANG schema output format */
+    LYS_OUT_YANG_COMPILED = 2, /**< YANG schema output format of the compiled schema tree */
+    LYS_OUT_YIN = 3,     /**< YIN schema output format */
+
+    LYS_OUT_TREE,        /**< Tree schema output format, for more information see the [printers](@ref howtoschemasprinters) page */
+} LYS_OUTFORMAT;
+
+/**
  * @brief Schema module printer.
  *
  * @param[in] out Printer handler for a specific output. Use ly_out_*() functions to create and free the handler.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 826e025..7bafc82 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -20,7 +20,6 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <limits.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -37,6 +36,7 @@
 #include "tree.h"
 #include "tree_schema.h"
 #include "tree_schema_internal.h"
+#include "parser_internal.h"
 
 API const struct lysc_node *
 lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, int options)
@@ -737,7 +737,7 @@
     mod->implemented = value;
 
     /* compile the schema */
-    LY_CHECK_RET(lys_compile(mod, LYSC_OPT_INTERNAL));
+    LY_CHECK_RET(lys_compile(&mod, LYSC_OPT_INTERNAL));
 
     return LY_SUCCESS;
 }
@@ -992,165 +992,109 @@
 }
 
 API struct lys_module *
-lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
+lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format)
 {
     struct lys_module *mod;
+    char *filename, *rev, *dot;
+    size_t len;
 
-    mod = lys_parse_mem_module(ctx, data, format, 1, NULL, NULL);
+    LY_CHECK_ARG_RET(NULL, ctx, in, format > LYS_IN_UNKNOWN, NULL);
+
+    mod = lys_parse_mem_module(ctx, in->current, format, 1, NULL, NULL);
     LY_CHECK_RET(!mod, NULL);
 
-    if (lys_compile(mod, 0)) {
-        ly_set_rm(&ctx->list, mod, NULL);
-        lys_module_free(mod, NULL);
-        return NULL;
+    switch (in->type) {
+    case LY_IN_FILEPATH:
+        /* check that name and revision match filename */
+        filename = strrchr(in->method.fpath.filepath, '/');
+        if (!filename) {
+            filename = in->method.fpath.filepath;
+        } else {
+            filename++;
+        }
+        rev = strchr(filename, '@');
+        dot = strrchr(filename, '.');
+
+        /* name */
+        len = strlen(mod->name);
+        if (strncmp(filename, mod->name, len) ||
+                ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
+            LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
+        }
+        if (rev) {
+            len = dot - ++rev;
+            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].date : "none");
+            }
+        }
+
+        break;
+    case LY_IN_FD:
+    case LY_IN_FILE:
+    case LY_IN_MEMORY:
+        /* nothing special to do */
+        break;
+    default:
+        LOGINT(ctx);
+        break;
     }
+
+    lys_parser_fill_filepath(ctx, in, &mod->filepath);
+    lys_compile(&mod, 0);
+
     return mod;
 }
 
-static void
-lys_parse_set_filename(struct ly_ctx *ctx, const char **filename, int fd)
+API struct lys_module *
+lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
 {
-    char path[PATH_MAX];
+	LY_ERR ret;
+    struct ly_in *in = NULL;
+    struct lys_module *result = NULL;
 
-#ifdef __APPLE__
-    if (fcntl(fd, F_GETPATH, path) != -1) {
-        *filename = lydict_insert(ctx, path, 0);
-    }
-#else
-    int len;
-    char proc_path[32];
+    LY_CHECK_ARG_RET(ctx, data, format != LYS_IN_UNKNOWN, NULL);
 
-    /* get URI if there is /proc */
-    sprintf(proc_path, "/proc/self/fd/%d", fd);
-    if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
-        *filename = lydict_insert(ctx, path, len);
-    }
-#endif
-}
+    LY_CHECK_ERR_RET(ret = ly_in_new_memory(data, &in), LOGERR(ctx, ret, "Unable to create input handler."), NULL);
 
-void *
-lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct lys_parser_ctx *main_ctx,
-              lys_custom_check custom_check, void *check_data)
-{
-    void *result;
-    struct lys_module *mod = NULL;
-    struct lysp_submodule *submod = NULL;
-    size_t length;
-    char *addr;
-
-    LY_CHECK_ARG_RET(ctx, ctx, NULL);
-    if (fd < 0) {
-        LOGARG(ctx, fd);
-        return NULL;
-    }
-
-    LY_CHECK_RET(ly_mmap(ctx, fd, &length, (void **)&addr), NULL);
-    if (!addr) {
-        LOGERR(ctx, LY_EINVAL, "Empty schema file.");
-        return NULL;
-    }
-
-    if (main_ctx) {
-        result = submod = lys_parse_mem_submodule(ctx, addr, format, main_ctx, custom_check, check_data);
-    } else {
-        result = mod = lys_parse_mem_module(ctx, addr, format, implement, custom_check, check_data);
-        if (mod && implement && lys_compile(mod, 0)) {
-            ly_set_rm(&ctx->list, mod, NULL);
-            lys_module_free(mod, NULL);
-            result = mod = NULL;
-        }
-    }
-    ly_munmap(addr, length);
-
-    if (mod && !mod->filepath) {
-        lys_parse_set_filename(ctx, &mod->filepath, fd);
-    } else if (submod && !submod->filepath) {
-        lys_parse_set_filename(ctx, &submod->filepath, fd);
-    }
+    result = lys_parse(ctx, in, format);
+    ly_in_free(in, 0);
 
     return result;
 }
 
-struct lys_module *
-lys_parse_fd_module(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, lys_custom_check custom_check,
-                    void *check_data)
-{
-    return (struct lys_module*)lys_parse_fd_(ctx, fd, format, implement, NULL, custom_check, check_data);
-}
-
-struct lysp_submodule *
-lys_parse_fd_submodule(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
-                       lys_custom_check custom_check, void *check_data)
-{
-    assert(main_ctx);
-    return (struct lysp_submodule*)lys_parse_fd_(ctx, fd, format, 0, main_ctx, custom_check, check_data);
-}
-
 API struct lys_module *
 lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format)
 {
-    return lys_parse_fd_module(ctx, fd, format, 1, NULL, NULL);
-}
+	LY_ERR ret;
+    struct ly_in *in = NULL;
+    struct lys_module *result = NULL;
 
-struct lys_module *
-lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement,
-                lys_custom_check custom_check, void *check_data)
-{
-    int fd;
-    struct lys_module *mod;
-    const char *rev, *dot, *filename;
-    size_t len;
+    LY_CHECK_ARG_RET(ctx, fd != -1, format != LYS_IN_UNKNOWN, NULL);
 
-    LY_CHECK_ARG_RET(ctx, ctx, path, NULL);
+    LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, &in), LOGERR(ctx, ret, "Unable to create input handler."), NULL);
 
-    fd = open(path, O_RDONLY);
-    LY_CHECK_ERR_RET(fd == -1, LOGERR(ctx, LY_ESYS, "Opening file \"%s\" failed (%s).", path, strerror(errno)), NULL);
+    result = lys_parse(ctx, in, format);
+    ly_in_free(in, 0);
 
-    mod = lys_parse_fd_module(ctx, fd, format, implement, custom_check, check_data);
-    close(fd);
-    LY_CHECK_RET(!mod, NULL);
-
-    /* check that name and revision match filename */
-    filename = strrchr(path, '/');
-    if (!filename) {
-        filename = path;
-    } else {
-        filename++;
-    }
-    rev = strchr(filename, '@');
-    dot = strrchr(filename, '.');
-
-    /* name */
-    len = strlen(mod->name);
-    if (strncmp(filename, mod->name, len) ||
-            ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
-        LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
-    }
-    if (rev) {
-        len = dot - ++rev;
-        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].date : "none");
-        }
-    }
-
-    if (!mod->filepath) {
-        /* store URI */
-        char rpath[PATH_MAX];
-        if (realpath(path, rpath) != NULL) {
-            mod->filepath = lydict_insert(ctx, rpath, 0);
-        } else {
-            mod->filepath = lydict_insert(ctx, path, 0);
-        }
-    }
-
-    return mod;
+    return result;
 }
 
 API struct lys_module *
 lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format)
 {
-    return lys_parse_path_(ctx, path, format, 1, NULL, NULL);
+	LY_ERR ret;
+    struct ly_in *in = NULL;
+    struct lys_module *result = NULL;
+
+    LY_CHECK_ARG_RET(ctx, path, format != LYS_IN_UNKNOWN, NULL);
+
+    LY_CHECK_ERR_RET(ret = ly_in_new_filepath(path, 0, &in), LOGERR(ctx, ret, "Unable to create input handler for filepath %s.", path), NULL);
+
+    result = lys_parse(ctx, in, format);
+    ly_in_free(in, 0);
+
+    return result;
 }
 
 API LY_ERR
diff --git a/src/tree_schema.h b/src/tree_schema.h
index bc573c8..a307e2d 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -126,27 +126,6 @@
         } \
     } } \
 
-/**
- * @brief Schema input formats accepted by libyang [parser functions](@ref howtoschemasparsers).
- */
-typedef enum {
-    LYS_IN_UNKNOWN = 0,  /**< unknown format, used as return value in case of error */
-    LYS_IN_YANG = 1,     /**< YANG schema input format */
-    LYS_IN_YIN = 3       /**< YIN schema input format */
-} LYS_INFORMAT;
-
-/**
- * @brief Schema output formats accepted by libyang [printer functions](@ref howtoschemasprinters).
- */
-typedef enum {
-    LYS_OUT_UNKNOWN = 0, /**< unknown format, used as return value in case of error */
-    LYS_OUT_YANG = 1,    /**< YANG schema output format */
-    LYS_OUT_YANG_COMPILED = 2, /**< YANG schema output format of the compiled schema tree */
-    LYS_OUT_YIN = 3,     /**< YIN schema output format */
-
-    LYS_OUT_TREE,        /**< Tree schema output format, for more information see the [printers](@ref howtoschemasprinters) page */
-} LYS_OUTFORMAT;
-
 #define LY_REV_SIZE 11   /**< revision data string length (including terminating NULL byte) */
 
 #define LYS_UNKNOWN 0x0000        /**< uninitalized unknown statement node */
@@ -1876,57 +1855,6 @@
 int lys_feature_value(const struct lys_module *module, const char *feature);
 
 /**
- * @brief Load a schema into the specified context.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] data The string containing the dumped data model in the specified
- * format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format);
-
-/**
- * @brief Read a schema from file descriptor into the specified context.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
- *            in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format);
-
-/**
- * @brief Read a schema into the specified context from a file.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] path Path to the file with the model in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format);
-
-/**
- * @brief Search for the schema file in the specified searchpaths.
- *
- * @param[in] searchpaths NULL-terminated array of paths to be searched (recursively). Current working
- * directory is searched automatically (but non-recursively if not in the provided list). Caller can use
- * result of the ly_ctx_get_searchdirs().
- * @param[in] cwd Flag to implicitly search also in the current working directory (non-recursively).
- * @param[in] name Name of the schema to find.
- * @param[in] revision Revision of the schema to find. If NULL, the newest found schema filepath is returned.
- * @param[out] localfile Mandatory output variable containing absolute path of the found schema. If no schema
- * complying the provided restriction is found, NULL is set.
- * @param[out] format Optional output variable containing expected format of the schema document according to the
- * file suffix.
- * @return LY_ERR value (LY_SUCCESS is returned even if the file is not found, then the *localfile is NULL).
- */
-LY_ERR lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision, char **localfile, LYS_INFORMAT *format);
-
-/**
  * @brief Get next schema tree (sibling) node element that can be instantiated in a data tree. Returned node can
  * be from an augment.
  *
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 058b19a..f359111 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -27,6 +27,7 @@
 #include "dict.h"
 #include "log.h"
 #include "path.h"
+#include "parser.h"
 #include "plugins_exts.h"
 #include "plugins_types.h"
 #include "plugins_exts_internal.h"
@@ -920,13 +921,17 @@
     if (!imp->module->parsed) {
         /* try to use filepath if present */
         if (imp->module->filepath) {
-            mod = (struct lys_module*)lys_parse_path(ctx->ctx, imp->module->filepath,
-                                 !strcmp(&imp->module->filepath[strlen(imp->module->filepath - 4)], ".yin") ? LYS_IN_YIN : LYS_IN_YANG);
-            if (mod != imp->module) {
-                LOGERR(ctx->ctx, LY_EINT, "Filepath \"%s\" of the module \"%s\" does not match.",
-                       imp->module->filepath, imp->module->name);
-                mod = NULL;
+            struct ly_in *in;
+            if (ly_in_new_filepath(imp->module->filepath, 0, &in)) {
+                LOGINT(ctx->ctx);
+            } else {
+                mod = lys_parse(ctx->ctx, in, !strcmp(&imp->module->filepath[strlen(imp->module->filepath - 4)], ".yin") ? LYS_IN_YIN : LYS_IN_YANG);
+                if (mod != imp->module) {
+                    LOGERR(ctx->ctx, LY_EINT, "Filepath \"%s\" of the module \"%s\" does not match.", imp->module->filepath, imp->module->name);
+                    mod = NULL;
+                }
             }
+            ly_in_free(in, 1);
         }
         if (!mod) {
             if (lysp_load_module(ctx->ctx, imp->module->name, imp->module->revision, 0, 1, &mod)) {
@@ -7024,7 +7029,7 @@
 }
 
 LY_ERR
-lys_compile(struct lys_module *mod, int options)
+lys_compile(struct lys_module **mod, int options)
 {
     struct lysc_ctx ctx = {0};
     struct lysc_module *mod_c;
@@ -7037,25 +7042,25 @@
     uint32_t i;
     LY_ERR ret = LY_SUCCESS;
 
-    LY_CHECK_ARG_RET(NULL, mod, mod->parsed, mod->ctx, LY_EINVAL);
+    LY_CHECK_ARG_RET(NULL, mod, *mod, (*mod)->parsed, (*mod)->ctx, LY_EINVAL);
 
-    if (!mod->implemented) {
+    if (!(*mod)->implemented) {
         /* just imported modules are not compiled */
         return LY_SUCCESS;
     }
 
-    sp = mod->parsed;
+    sp = (*mod)->parsed;
 
-    ctx.ctx = mod->ctx;
-    ctx.mod = mod;
-    ctx.mod_def = mod;
+    ctx.ctx = (*mod)->ctx;
+    ctx.mod = *mod;
+    ctx.mod_def = *mod;
     ctx.options = options;
     ctx.path_len = 1;
     ctx.path[0] = '/';
 
-    mod->compiled = mod_c = calloc(1, sizeof *mod_c);
-    LY_CHECK_ERR_RET(!mod_c, LOGMEM(mod->ctx), LY_EMEM);
-    mod_c->mod = mod;
+    (*mod)->compiled = mod_c = calloc(1, sizeof *mod_c);
+    LY_CHECK_ERR_RET(!mod_c, LOGMEM((*mod)->ctx), LY_EMEM);
+    mod_c->mod = *mod;
 
     COMPILE_ARRAY_GOTO(&ctx, sp->imports, mod_c->imports, u, lys_compile_import, ret, error);
     LY_ARRAY_FOR(sp->includes, u) {
@@ -7064,10 +7069,10 @@
     }
 
     /* features */
-    if (mod->off_features) {
+    if ((*mod)->off_features) {
         /* there is already precompiled array of features */
-        mod_c->features = mod->off_features;
-        mod->off_features = NULL;
+        mod_c->features = (*mod)->off_features;
+        (*mod)->off_features = NULL;
     } else {
         /* features are compiled directly into the compiled module structure,
          * but it must be done in two steps to allow forward references (via if-feature) between the features themselves */
@@ -7096,7 +7101,7 @@
     lysc_update_path(&ctx, NULL, "{identity}");
     COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, u, lys_compile_identity, ret, error);
     if (sp->identities) {
-        LY_CHECK_RET(lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities));
+        LY_CHECK_GOTO(ret = lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities), error);
     }
     lysc_update_path(&ctx, NULL, NULL);
 
@@ -7159,8 +7164,8 @@
 #endif
 
     /* add ietf-netconf-with-defaults "default" metadata to the compiled module */
-    if (!strcmp(mod->name, "ietf-netconf-with-defaults")) {
-        LY_CHECK_GOTO(ret = lys_compile_ietf_netconf_wd_annotation(&ctx, mod), error);
+    if (!strcmp((*mod)->name, "ietf-netconf-with-defaults")) {
+        LY_CHECK_GOTO(ret = lys_compile_ietf_netconf_wd_annotation(&ctx, *mod), error);
     }
 
     ly_set_erase(&ctx.dflts, free);
@@ -7171,8 +7176,8 @@
     LY_ARRAY_FREE(augments);
 
     if (ctx.options & LYSC_OPT_FREE_SP) {
-        lysp_module_free(mod->parsed);
-        ((struct lys_module*)mod)->parsed = NULL;
+        lysp_module_free((*mod)->parsed);
+        (*mod)->parsed = NULL;
     }
 
     if (!(ctx.options & LYSC_OPT_INTERNAL)) {
@@ -7185,11 +7190,11 @@
         }
     }
 
-    ((struct lys_module*)mod)->compiled = mod_c;
+    (*mod)->compiled = mod_c;
     return LY_SUCCESS;
 
 error:
-    lys_feature_precompile_revert(&ctx, mod);
+    lys_feature_precompile_revert(&ctx, *mod);
     ly_set_erase(&ctx.dflts, free);
     ly_set_erase(&ctx.xpath, NULL);
     ly_set_erase(&ctx.leafrefs, NULL);
@@ -7197,7 +7202,7 @@
     ly_set_erase(&ctx.tpdf_chain, NULL);
     LY_ARRAY_FREE(augments);
     lysc_module_free(mod_c, NULL);
-    mod->compiled = NULL;
+    (*mod)->compiled = NULL;
 
     /* revert compilation of modules implemented by dependency */
     for (i = 0; i < ctx.ctx->list.count; ++i) {
@@ -7213,5 +7218,10 @@
         }
     }
 
+    /* remove the module itself from the context and free it */
+    ly_set_rm(&ctx.ctx->list, *mod, NULL);
+    lys_module_free(*mod, NULL);
+    *mod = NULL;
+
     return ret;
 }
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 664edd8..fa32f8f 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -32,6 +32,8 @@
 #include "hash_table.h"
 #include "log.h"
 #include "plugins_exts.h"
+#include "parser.h"
+#include "parser_internal.h"
 #include "set.h"
 #include "tree.h"
 #include "tree_schema.h"
@@ -749,14 +751,12 @@
 lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement,
                      struct lys_parser_ctx *main_ctx, const char *main_name, int required, void **result)
 {
-    int fd;
+    struct ly_in *in;
     char *filepath = NULL;
-    const char **fp;
     LYS_INFORMAT format;
     void *mod = NULL;
     LY_ERR ret = LY_SUCCESS;
     struct lysp_load_module_check_data check_data = {0};
-    char rpath[PATH_MAX];
 
     LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision,
                                       &filepath, &format));
@@ -765,31 +765,29 @@
 
     LOGVRB("Loading schema from \"%s\" file.", filepath);
 
-    /* open the file */
-    fd = open(filepath, O_RDONLY);
-    LY_CHECK_ERR_GOTO(fd < 0, LOGERR(ctx, LY_ESYS, "Unable to open data model file \"%s\" (%s).",
-                                     filepath, strerror(errno)); ret = LY_ESYS, cleanup);
-
+    /* get the (sub)module */
+    LY_CHECK_ERR_GOTO(ret = ly_in_new_filepath(filepath, 0, &in), LOGERR(ctx, ret, "Unable to create input handler for filepath %s.", filepath), cleanup);
     check_data.name = name;
     check_data.revision = revision;
     check_data.path = filepath;
     check_data.submoduleof = main_name;
-    mod = lys_parse_fd_(ctx, fd, format, implement, main_ctx,
-                        lysp_load_module_check, &check_data);
-    close(fd);
-    LY_CHECK_ERR_GOTO(!mod, ly_errcode(ctx), cleanup);
+    if (main_ctx) {
+        mod = lys_parse_mem_submodule(ctx, in->current, format, main_ctx, lysp_load_module_check, &check_data);
+    } else {
+        mod = lys_parse_mem_module(ctx, in->current, format, implement, lysp_load_module_check, &check_data);
+
+    }
+    LY_CHECK_ERR_GOTO(!mod, ly_in_free(in, 1);ly_errcode(ctx), cleanup);
 
     if (main_ctx) {
-        fp = &((struct lysp_submodule*)mod)->filepath;
+        lys_parser_fill_filepath(ctx, in, &((struct lysp_submodule*)mod)->filepath);
     } else {
-        fp = &((struct lys_module*)mod)->filepath;
+        lys_parser_fill_filepath(ctx, in, &((struct lys_module*)mod)->filepath);
     }
-    if (!(*fp)) {
-        if (realpath(filepath, rpath) != NULL) {
-            (*fp) = lydict_insert(ctx, rpath, 0);
-        } else {
-            (*fp) = lydict_insert(ctx, filepath, 0);
-        }
+    ly_in_free(in, 1);
+
+    if (mod && implement) {
+        lys_compile((struct lys_module**)&mod, 0);
     }
 
     *result = mod;
@@ -858,10 +856,8 @@
                     if (module_data_free) {
                         module_data_free((void*)module_data, ctx->imp_clb_data);
                     }
-                    if (*mod && implement && lys_compile(*mod, 0)) {
-                        ly_set_rm(&ctx->list, *mod, NULL);
-                        lys_module_free(*mod, NULL);
-                        *mod = NULL;
+                    if (*mod && implement) {
+                        lys_compile(mod, 0);
                     }
                 }
             }
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index c4370b6..ddc7966 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -340,12 +340,13 @@
 /**
  * @brief Compile printable schema into a validated schema linking all the references.
  *
- * @param[in, out] mod Schema structure holding pointers to both schema structure types. The ::lys_module#parsed
+ * @param[in, out] mod Pointer to the schema structure holding pointers to both schema structure types. The ::lys_module#parsed
  * member is used as input and ::lys_module#compiled is used to hold the result of the compilation.
+ * If the compilation fails, the whole module is removed from context, freed and @p mod is set to NULL!
  * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
-LY_ERR lys_compile(struct lys_module *mod, int options);
+LY_ERR lys_compile(struct lys_module **mod, int options);
 
 /**
  * @brief Get address of a node's actions list if any.
@@ -556,81 +557,13 @@
                                                lys_custom_check custom_check, void *check_data);
 
 /**
- * @brief Parse module or submodule from a file descriptor.
+ * @brief Fill filepath value if available in input handler @p in
  *
- * The modules are added into the context, submodules not. The latest_revision flag is updated in both cases.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
- *            in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @param[in] implement Flag if the schema is supposed to be marked as implemented.
- * @param[in] main_ctx Parser context of the main module in case of parsing submodule. This flag decides if the module
- * or submodule was expected to be parsed.
- * @param[in] custom_check Callback to check the parsed schema before it is accepted.
- * @param[in] check_data Caller's data to pass to the custom_check callback.
- * @return Pointer to the data model structure or NULL on error.
+ * @param[in] ctx Context with dictionary where the filepath value will be stored.
+ * @param[in] in Input handler to examine (filepath is not available for all the input types).
+ * @param[out] filepath Address of the variable where the filepath is stored.
  */
-void *lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct lys_parser_ctx *main_ctx,
-                    lys_custom_check custom_check, void *check_data);
-
-/**
- * @brief Parse YANG module from a file descriptor.
- *
- * The modules are added into the context. The latest_revision flag is updated.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
- *            in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @param[in] implement Flag if the schema is supposed to be marked as implemented.
- * @param[in] custom_check Callback to check the parsed schema before it is accepted.
- * @param[in] check_data Caller's data to pass to the custom_check callback.
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_fd_module(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement,
-                                       lys_custom_check custom_check, void *check_data);
-
-/**
- * @brief Parse submodule from a file descriptor.
- *
- * The latest_revision flag of submodules is updated.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
- *            in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @param[in] main_ctx Parser context of the main module.
- * @param[in] custom_check Callback to check the parsed schema before it is accepted.
- * @param[in] check_data Caller's data to pass to the custom_check callback.
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lysp_submodule *lys_parse_fd_submodule(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
-                                              lys_custom_check custom_check, void *check_data);
-
-/**
- * @brief Parse YANG module from a filepath.
- *
- * The modules are added into the context. The latest_revision flag is updated.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] path Path to the file with the model in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @param[in] implement Flag if the schema is supposed to be marked as implemented.
- * @param[in] custom_check Callback to check the parsed schema before it is accepted.
- * @param[in] check_data Caller's data to pass to the custom_check callback.
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement,
-                                   lys_custom_check custom_check, void *check_data);
+void lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath);
 
 /**
  * @brief Load the (sub)module into the context.
@@ -828,53 +761,6 @@
 void lys_module_free(struct lys_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv));
 
 /**
- * @brief Parse submodule from YANG data.
- * @param[in,out] ctx Parser context.
- * @param[in] ly_ctx Context of YANG schemas.
- * @param[in] main_ctx Parser context of main module.
- * @param[in] data Input data to be parsed.
- * @param[out] submod Pointer to the parsed submodule structure.
- * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
- */
-LY_ERR yang_parse_submodule(struct lys_yang_parser_ctx **context, struct ly_ctx *ly_ctx, struct lys_parser_ctx *main_ctx,
-                            const char *data, struct lysp_submodule **submod);
-
-/**
- * @brief Parse module from YANG data.
- * @param[in] ctx Parser context.
- * @param[in] data Input data to be parsed.
- * @param[in, out] mod Prepared module structure where the parsed information, including the parsed
- * module structure, will be filled in.
- * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
- */
-LY_ERR yang_parse_module(struct lys_yang_parser_ctx **context, const char *data, struct lys_module *mod);
-
-/**
- * @brief Parse module from YIN data.
- *
- * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
- * @param[in] data Input data to be parsed.
- * @param[in,out] mod Prepared module structure where the parsed information, including the parsed
- * module structure, will be filled in.
- *
- * @return LY_ERR values.
- */
-LY_ERR yin_parse_module(struct lys_yin_parser_ctx **yin_ctx, const char *data, struct lys_module *mod);
-
-/**
- * @brief Parse submodule from YIN data.
- *
- * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
- * @param[in] ctx Libyang context.
- * @param[in] main_ctx Parser context of main module.
- * @param[in,out] data Input data to be parsed.
- * @param[in,out] submod Submodule structure where the parsed information, will be filled in.
- * @return LY_ERR values.
- */
-LY_ERR yin_parse_submodule(struct lys_yin_parser_ctx **yin_ctx, struct ly_ctx *ctx, struct lys_parser_ctx *main_ctx,
-                           const char *data, struct lysp_submodule **submod);
-
-/**
  * @brief Make the specific module implemented, use the provided value as flag.
  *
  * @param[in] mod Module to make implemented. It is not an error to provide already implemented module, it just does nothing.
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
index 39b1854..9a5905a 100644
--- a/tests/utests/CMakeLists.txt
+++ b/tests/utests/CMakeLists.txt
@@ -2,6 +2,7 @@
     utest:test_common
     utest:test_set
     utest:test_hash_table
+    utest:test_inout
     utest:test_context
     utest:test_xml
     utest:test_xpath
@@ -41,6 +42,7 @@
     " "
     " "
     " "
+    " "
     " ")
 set(tests ${tests} ${local_tests} PARENT_SCOPE)
 set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/utests/schema/test_parser_yang.c b/tests/utests/schema/test_parser_yang.c
index 6311dfc..31b9c34 100644
--- a/tests/utests/schema/test_parser_yang.c
+++ b/tests/utests/schema/test_parser_yang.c
@@ -23,6 +23,7 @@
 #include "../../../src/common.h"
 #include "../../../src/tree_schema.h"
 #include "../../../src/tree_schema_internal.h"
+#include "../../../src/parser_internal.h"
 
 /* originally static functions from tree_schema_free.c and parser_yang.c */
 void lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext);
diff --git a/tests/utests/schema/test_parser_yin.c b/tests/utests/schema/test_parser_yin.c
index 809bfd6..d2576b9 100644
--- a/tests/utests/schema/test_parser_yin.c
+++ b/tests/utests/schema/test_parser_yin.c
@@ -24,6 +24,7 @@
 #include "../../../src/common.h"
 #include "../../../src/tree_schema.h"
 #include "../../../src/tree_schema_internal.h"
+#include "../../../src/parser_internal.h"
 #include "../../../src/parser_yin.h"
 #include "../../../src/xml.h"
 #include "../../../src/xpath.h"
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index 9c90ce5..ea6166e 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -20,10 +20,11 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "../../src/common.h"
-#include "../../src/tree_schema_internal.h"
-#include "../../src/xpath.h"
-#include "../../src/plugins_types.h"
+#include "../../../src/common.h"
+#include "../../../src/tree_schema_internal.h"
+#include "../../../src/parser_internal.h"
+#include "../../../src/xpath.h"
+#include "../../../src/plugins_types.h"
 
 void lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat);
 void yang_parser_ctx_free(struct lys_yang_parser_ctx *ctx);
@@ -96,97 +97,60 @@
 }
 
 static void
-reset_mod(struct lys_module *module)
-{
-    struct ly_ctx *ctx = module->ctx;
-    lysc_module_free(module->compiled, NULL);
-    lysp_module_free(module->parsed);
-
-    FREE_STRING(ctx, module->name);
-    FREE_STRING(ctx, module->ns);
-    FREE_STRING(ctx, module->prefix);
-    FREE_STRING(ctx, module->filepath);
-    FREE_STRING(ctx, module->org);
-    FREE_STRING(ctx, module->contact);
-    FREE_STRING(ctx, module->dsc);
-    FREE_STRING(ctx, module->ref);
-    FREE_ARRAY(ctx, module->off_features, lysc_feature_free);
-
-    memset(module, 0, sizeof *module);
-    module->ctx = ctx;
-    module->implemented = 1;
-}
-
-static void
 test_module(void **state)
 {
     *state = test_module;
 
     const char *str;
-    struct lys_yang_parser_ctx *ctx = NULL;
-    struct lys_module mod = {0};
+    struct ly_ctx *ctx = NULL;
+    struct lys_module *mod = NULL;
     struct lysc_feature *f;
     struct lysc_iffeature *iff;
 
     str = "module test {namespace urn:test; prefix t;"
           "feature f1;feature f2 {if-feature f1;}}";
-    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &mod.ctx));
-    reset_mod(&mod);
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
 
     assert_int_equal(LY_EINVAL, lys_compile(NULL, 0));
     logbuf_assert("Invalid argument mod (lys_compile()).");
     assert_int_equal(LY_EINVAL, lys_compile(&mod, 0));
-    logbuf_assert("Invalid argument mod->parsed (lys_compile()).");
-    assert_int_equal(LY_SUCCESS, yang_parse_module(&ctx, str, &mod));
-    yang_parser_ctx_free(ctx);
-    mod.implemented = 0;
+    logbuf_assert("Invalid argument *mod (lys_compile()).");
+    assert_non_null(mod = lys_parse_mem_module(ctx, str, LYS_IN_YANG, 0, NULL, NULL));
+    assert_int_equal(0, mod->implemented);
     assert_int_equal(LY_SUCCESS, lys_compile(&mod, 0));
-    assert_null(mod.compiled);
-    mod.implemented = 1;
+    assert_null(mod->compiled);
+    mod->implemented = 1;
     assert_int_equal(LY_SUCCESS, lys_compile(&mod, 0));
-    assert_non_null(mod.compiled);
-    assert_string_equal("test", mod.name);
-    assert_string_equal("urn:test", mod.ns);
-    assert_string_equal("t", mod.prefix);
+    assert_non_null(mod->compiled);
+    assert_string_equal("test", mod->name);
+    assert_string_equal("urn:test", mod->ns);
+    assert_string_equal("t", mod->prefix);
     /* features */
-    assert_non_null(mod.compiled->features);
-    assert_int_equal(2, LY_ARRAY_SIZE(mod.compiled->features));
-    f = &mod.compiled->features[1];
+    assert_non_null(mod->compiled->features);
+    assert_int_equal(2, LY_ARRAY_SIZE(mod->compiled->features));
+    f = &mod->compiled->features[1];
     assert_non_null(f->iffeatures);
     assert_int_equal(1, LY_ARRAY_SIZE(f->iffeatures));
     iff = &f->iffeatures[0];
     assert_non_null(iff->expr);
     assert_non_null(iff->features);
     assert_int_equal(1, LY_ARRAY_SIZE(iff->features));
-    assert_ptr_equal(&mod.compiled->features[0], iff->features[0]);
-
-    lysc_module_free(mod.compiled, NULL);
-
-    assert_int_equal(LY_SUCCESS, lys_compile(&mod, LYSC_OPT_FREE_SP));
-    assert_non_null(mod.compiled);
-
-    lysc_module_free(mod.compiled, NULL);
-    mod.compiled = NULL;
+    assert_ptr_equal(&mod->compiled->features[0], iff->features[0]);
 
     /* submodules cannot be compiled directly */
     str = "submodule test {belongs-to xxx {prefix x;}}";
-    assert_int_equal(LY_EINVAL, yang_parse_module(&ctx, str, &mod));
-    yang_parser_ctx_free(ctx);
+    assert_null(lys_parse_mem_module(ctx, str, LYS_IN_YANG, 1, NULL, NULL));
     logbuf_assert("Input data contains submodule which cannot be parsed directly without its main module.");
-    assert_null(mod.parsed);
-    reset_mod(&mod);
 
     /* data definition name collision in top level */
-    assert_int_equal(LY_SUCCESS, yang_parse_module(&ctx, "module aa {namespace urn:aa;prefix aa;"
-                                                  "leaf a {type string;} container a{presence x;}}", &mod));
-    yang_parser_ctx_free(ctx);
+    assert_non_null(mod = lys_parse_mem_module(ctx, "module aa {namespace urn:aa;prefix aa;"
+                                        "leaf a {type string;} container a{presence x;}}", LYS_IN_YANG, 1, NULL, NULL));
     assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
     logbuf_assert("Duplicate identifier \"a\" of data definition/RPC/action/notification statement. /aa:a");
-    assert_null(mod.compiled);
-    reset_mod(&mod);
+    assert_null(mod);
 
     *state = NULL;
-    ly_ctx_destroy(mod.ctx, NULL);
+    ly_ctx_destroy(ctx, NULL);
 }
 
 
diff --git a/tests/utests/test_context.c b/tests/utests/test_context.c
index fe06f96..d55e5b1 100644
--- a/tests/utests/test_context.c
+++ b/tests/utests/test_context.c
@@ -325,17 +325,23 @@
     /* reloading module in case only the compiled module resists in the context */
     mod1 = lys_parse_mem_module(ctx, "module w {namespace urn:w;prefix w;revision 2018-10-24;}", LYS_IN_YANG, 1, NULL, NULL);
     assert_non_null(mod1);
-    assert_int_equal(LY_SUCCESS, lys_compile(mod1, LYSC_OPT_FREE_SP));
+    assert_int_equal(LY_SUCCESS, lys_compile(&mod1, LYSC_OPT_FREE_SP));
     assert_non_null(mod1->compiled);
     assert_null(mod1->parsed);
+
     mod2 = lys_parse_mem_module(ctx, "module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", LYS_IN_YANG, 1, NULL, NULL);
     assert_non_null(mod2);
     /* mod1->parsed is necessary to compile mod2 because of possible groupings, typedefs, ... */
     ly_ctx_set_module_imp_clb(ctx, NULL, NULL);
-    assert_int_equal(LY_ENOTFOUND, lys_compile(mod2, 0));
+    assert_int_equal(LY_ENOTFOUND, lys_compile(&mod2, 0));
     logbuf_assert("Unable to reload \"w\" module to import it into \"z\", source data not found.");
+    assert_null(mod2);
+
+    mod2 = lys_parse_mem_module(ctx, "module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", LYS_IN_YANG, 1, NULL, NULL);
+    assert_non_null(mod2);
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "module w {namespace urn:w;prefix w;revision 2018-10-24;}");
-    assert_int_equal(LY_SUCCESS, lys_compile(mod2, 0));
+    assert_int_equal(LY_SUCCESS, lys_compile(&mod2, 0));
+    assert_non_null(mod2);
     assert_non_null(mod1->parsed);
     assert_string_equal("w", mod1->name);
 
diff --git a/tests/utests/test_inout.c b/tests/utests/test_inout.c
new file mode 100644
index 0000000..cbec9c7
--- /dev/null
+++ b/tests/utests/test_inout.c
@@ -0,0 +1,438 @@
+/*
+ * @file test_inout.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for input and output handlers functions
+ *
+ * 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 "tests/config.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "../../src/common.h"
+#include "../../src/log.h"
+#include "../../src/printer.h"
+#include "../../src/parser.h"
+
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+#if ENABLE_LOGGER_CHECKING
+static void
+logger(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+    (void) level; /* unused */
+    if (store) {
+        if (path && path[0]) {
+            snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+        } else {
+            strncpy(logbuf, msg, BUFSIZE - 1);
+        }
+        if (store > 0) {
+            --store;
+        }
+    }
+}
+#endif
+
+static int
+logger_setup(void **state)
+{
+    (void) state; /* unused */
+
+    ly_set_log_clb(logger, 0);
+
+    return 0;
+}
+
+static int
+logger_teardown(void **state)
+{
+    (void) state; /* unused */
+#if ENABLE_LOGGER_CHECKING
+    if (*state) {
+        fprintf(stderr, "%s\n", logbuf);
+    }
+#endif
+    return 0;
+}
+
+void
+logbuf_clean(void)
+{
+    logbuf[0] = '\0';
+}
+
+#if ENABLE_LOGGER_CHECKING
+#   define logbuf_assert(str) assert_string_equal(logbuf, str)
+#else
+#   define logbuf_assert(str)
+#endif
+
+static void
+test_input_mem(void **state)
+{
+    struct ly_in *in = NULL;
+    char *str1 = "a", *str2 = "b";
+
+    *state = test_input_mem;
+
+    assert_int_equal(LY_EINVAL, ly_in_new_memory(NULL, NULL));
+    assert_int_equal(LY_EINVAL, ly_in_new_memory(str1, NULL));
+    assert_null(ly_in_memory(NULL, NULL));
+
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in));
+    assert_int_equal(LY_IN_MEMORY, ly_in_type(in));
+    assert_ptr_equal(str1, ly_in_memory(in, str2));
+    assert_ptr_equal(str2, ly_in_memory(in, NULL));
+    assert_ptr_equal(str2, ly_in_memory(in, NULL));
+    ly_in_free(in, 0);
+
+    /* cleanup */
+    *state = NULL;
+}
+
+static void
+test_input_fd(void **state)
+{
+    struct ly_in *in = NULL;
+    int fd1, fd2;
+    struct stat statbuf;
+
+    *state = test_input_fd;
+
+    assert_int_equal(LY_EINVAL, ly_in_new_fd(-1, NULL));
+    assert_int_equal(-1, ly_in_fd(NULL, -1));
+
+    assert_int_not_equal(-1, fd1 = open(__FILE__, O_RDONLY));
+    assert_int_not_equal(-1, fd2 = open(__FILE__, O_RDONLY));
+
+    assert_int_equal(LY_EINVAL, ly_in_new_fd(fd1, NULL));
+
+    assert_int_equal(LY_SUCCESS, ly_in_new_fd(fd1, &in));
+    assert_int_equal(LY_IN_FD, ly_in_type(in));
+    assert_ptr_equal(fd1, ly_in_fd(in, fd2));
+    assert_ptr_equal(fd2, ly_in_fd(in, -1));
+    assert_ptr_equal(fd2, ly_in_fd(in, -1));
+    ly_in_free(in, 1);
+    /* fd1 is still open */
+    assert_int_equal(0, fstat(fd1, &statbuf));
+    close(fd1);
+    /* but fd2 was closed by ly_in_free() */
+    errno = 0;
+    assert_int_equal(-1, fstat(fd2, &statbuf));
+    assert_int_equal(errno, EBADF);
+
+    /* cleanup */
+    *state = NULL;
+}
+
+static void
+test_input_file(void **state)
+{
+    struct ly_in *in = NULL;
+    FILE *f1 = NULL, *f2 = NULL;
+
+    *state = test_input_file;
+
+    assert_int_equal(LY_EINVAL, ly_in_new_file(NULL, NULL));
+    assert_null(ly_in_file(NULL, NULL));
+
+    assert_int_not_equal(-1, f1 = fopen(__FILE__, "r"));
+    assert_int_not_equal(-1, f2 = fopen(__FILE__, "r"));
+
+    assert_int_equal(LY_EINVAL, ly_in_new_file(f1, NULL));
+
+    assert_int_equal(LY_SUCCESS, ly_in_new_file(f1, &in));
+    assert_int_equal(LY_IN_FILE, ly_in_type(in));
+    assert_ptr_equal(f1, ly_in_file(in, f2));
+    assert_ptr_equal(f2, ly_in_file(in, NULL));
+    assert_ptr_equal(f2, ly_in_file(in, NULL));
+    ly_in_free(in, 1);
+    /* f1 is still open */
+    assert_int_not_equal(-1, fileno(f1));
+    fclose(f1);
+    /* but f2 was closed by ly_in_free() */
+
+    /* cleanup */
+    *state = NULL;
+}
+
+static void
+test_input_filepath(void **state)
+{
+    struct ly_in *in = NULL;
+    const char *path1 = __FILE__, *path2 = __FILE__;
+
+    *state = test_input_filepath;
+
+    assert_int_equal(LY_EINVAL, ly_in_new_filepath(NULL, 0, NULL));
+    assert_int_equal(LY_EINVAL, ly_in_new_filepath(path1, 0, NULL));
+    assert_ptr_equal(((void *)-1), ly_in_filepath(NULL, NULL, 0));
+
+    assert_int_equal(LY_SUCCESS, ly_in_new_filepath(path1, 0, &in));
+    assert_int_equal(LY_IN_FILEPATH, ly_in_type(in));
+    assert_ptr_equal(NULL, ly_in_filepath(in, path2, 0));
+    assert_string_equal(path2, ly_in_filepath(in, NULL, 0));
+    ly_in_free(in, 0);
+
+    /* cleanup */
+    *state = NULL;
+}
+
+static void
+test_output_mem(void **state)
+{
+    struct ly_out *out = NULL;
+    char *buf1 = NULL, *buf2 = NULL;
+
+    *state = test_output_mem;
+
+    /* manipulate with the handler */
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out));
+    assert_int_equal(LY_OUT_MEMORY, ly_out_type(out));
+    ly_write(out, "test", 4);
+    assert_ptr_equal(buf1, ly_out_memory(out, &buf2, 0));
+    assert_ptr_equal(buf2, ly_out_memory(out, NULL, 0));
+    assert_ptr_equal(buf2, ly_out_memory(out, &buf1, strlen(buf1)));
+    ly_out_free(out, NULL, 0);
+
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, strlen(buf1), &out));
+    ly_out_free(out, NULL, 1);
+
+    /* writing data */
+
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out));
+    assert_int_equal(10, ly_print(out, "test %s", "print"));
+    assert_string_equal("test print", buf1);
+    assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+    assert_int_equal(8, ly_write(out, "rewrite", 8));
+    assert_string_equal("rewrite", buf1);
+    ly_out_free(out, NULL, 1);
+
+    /* cleanup */
+    *state = NULL;
+}
+
+static void
+test_output_fd(void **state)
+{
+    struct ly_out *out = NULL;
+    int fd1, fd2;
+    char buf[31] = {0};
+    const char *filepath = "/tmp/libyang_test_output";
+
+    *state = test_output_fd;
+
+    assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+    assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+
+    /* manipulate with the handler */
+    assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+    assert_int_equal(LY_OUT_FD, ly_out_type(out));
+    assert_ptr_equal(fd1, ly_out_fd(out, fd2));
+    assert_ptr_equal(fd2, ly_out_fd(out, -1));
+    assert_ptr_equal(fd2, ly_out_fd(out, fd1));
+    ly_out_free(out, NULL, 0);
+    assert_int_equal(0, close(fd2));
+    assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+    ly_out_free(out, NULL, 1);
+
+    /* writing data */
+    assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+    assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+    /* truncate file to start with no data */
+    assert_int_equal(0, ftruncate(fd1, 0));
+
+    assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+    assert_int_equal(10, ly_print(out, "test %s", "print"));
+    ly_print_flush(out);
+    assert_int_equal(10, read(fd2, buf, 30));
+    assert_string_equal("test print", buf);
+    assert_int_equal(0, lseek(fd2, 0, SEEK_SET));
+    assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+    assert_int_equal(8, ly_write(out, "rewrite", 8));
+    ly_print_flush(out);
+    assert_int_equal(8, read(fd2, buf, 30));
+    assert_string_equal("rewrite", buf);
+
+    close(fd2);
+    ly_out_free(out, NULL, 1);
+
+    /* cleanup */
+    *state = NULL;
+}
+
+static void
+test_output_file(void **state)
+{
+    struct ly_out *out = NULL;
+    FILE *f1, *f2;
+    char buf[31] = {0};
+    const char *filepath = "/tmp/libyang_test_output";
+
+    *state = test_output_file;
+
+    assert_int_not_equal(-1, f1 = fopen(filepath, "w"));
+    assert_int_not_equal(-1, f2 = fopen(filepath, "w"));
+
+    /* manipulate with the handler */
+    assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+    assert_int_equal(LY_OUT_FILE, ly_out_type(out));
+    assert_ptr_equal(f1, ly_out_file(out, f2));
+    assert_ptr_equal(f2, ly_out_file(out, NULL));
+    assert_ptr_equal(f2, ly_out_file(out, f1));
+    ly_out_free(out, NULL, 0);
+    assert_int_equal(0, fclose(f2));
+    assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+    ly_out_free(out, NULL, 1);
+
+    /* writing data */
+    assert_int_not_equal(-1, f1 = fopen(filepath, "w"));
+    assert_int_not_equal(-1, f2 = fopen(filepath, "r"));
+
+    assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+    assert_int_equal(10, ly_print(out, "test %s", "print"));
+    ly_print_flush(out);
+    assert_non_null(fgets(buf, 31, f2));
+    assert_string_equal("test print", buf);
+    assert_int_equal(0, fseek(f2, 0, SEEK_SET));
+    assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+    assert_int_equal(8, ly_write(out, "rewrite", 8));
+    ly_print_flush(out);
+    assert_non_null(fgets(buf, 31, f2));
+    assert_string_equal("rewrite", buf);
+
+    fclose(f2);
+    ly_out_free(out, NULL, 1);
+
+    /* cleanup */
+    *state = NULL;
+}
+
+static void
+test_output_filepath(void **state)
+{
+    struct ly_out *out = NULL;
+    FILE *f1;
+    char buf[31] = {0};
+    const char *fp1 = "/tmp/libyang_test_output";
+    const char *fp2 = "/tmp/libyang_test_output2";
+
+    *state = test_output_filepath;
+
+    /* manipulate with the handler */
+    assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+    assert_int_equal(LY_OUT_FILEPATH, ly_out_type(out));
+    assert_ptr_equal(NULL, ly_out_filepath(out, fp2));
+    assert_string_equal(fp2, ly_out_filepath(out, NULL));
+    assert_ptr_equal(NULL, ly_out_filepath(out, fp1));
+    ly_out_free(out, NULL, 0);
+    assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+    ly_out_free(out, NULL, 1);
+
+    /* writing data */
+    assert_int_not_equal(-1, f1 = fopen(fp1, "r"));
+
+    assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+    assert_int_equal(10, ly_print(out, "test %s", "print"));
+    ly_print_flush(out);
+    assert_non_null(fgets(buf, 31, f1));
+    assert_string_equal("test print", buf);
+    assert_int_equal(0, fseek(f1, 0, SEEK_SET));
+    assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+    assert_int_equal(8, ly_write(out, "rewrite", 8));
+    ly_print_flush(out);
+    assert_non_null(fgets(buf, 31, f1));
+    assert_string_equal("rewrite", buf);
+
+    fclose(f1);
+    ly_out_free(out, NULL, 1);
+
+    /* cleanup */
+    *state = NULL;
+}
+
+static void
+test_output_clb(void **state)
+{
+    struct ly_out *out = NULL;
+    int fd1, fd2;
+    char buf[31] = {0};
+    const char *filepath = "/tmp/libyang_test_output";
+
+    *state = test_output_clb;
+
+    assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+    assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+
+    /* manipulate with the handler */
+    assert_int_equal(LY_SUCCESS, ly_out_new_clb((ssize_t (*)(void *, const void *, size_t))write, (void*)(intptr_t)fd1, &out));
+    assert_int_equal(LY_OUT_CALLBACK, ly_out_type(out));
+    assert_ptr_equal(fd1, ly_out_clb_arg(out, (void*)(intptr_t)fd2));
+    assert_ptr_equal(fd2, ly_out_clb_arg(out, NULL));
+    assert_ptr_equal(fd2, ly_out_clb_arg(out, (void*)(intptr_t)fd1));
+    assert_ptr_equal(write, ly_out_clb(out, (ssize_t (*)(void *, const void *, size_t))write));
+    ly_out_free(out, NULL, 0);
+    assert_int_equal(0, close(fd2));
+    assert_int_equal(LY_SUCCESS, ly_out_new_clb((ssize_t (*)(void *, const void *, size_t))write, (void*)(intptr_t)fd1, &out));
+    ly_out_free(out, (void (*)(void *))close, 0);
+
+    /* writing data */
+    assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+    assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+    /* truncate file to start with no data */
+    assert_int_equal(0, ftruncate(fd1, 0));
+
+    assert_int_equal(LY_SUCCESS, ly_out_new_clb((ssize_t (*)(void *, const void *, size_t))write, (void*)(intptr_t)fd1, &out));
+    assert_int_equal(10, ly_print(out, "test %s", "print"));
+    assert_int_equal(10, read(fd2, buf, 30));
+    assert_string_equal("test print", buf);
+
+    close(fd2);
+    ly_out_free(out, (void (*)(void *))close, 0);
+
+    /* cleanup */
+    *state = NULL;
+}
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_input_mem, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_input_fd, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_input_file, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_input_filepath, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_output_mem, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_output_fd, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_output_file, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_output_filepath, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_output_clb, logger_setup, logger_teardown),
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}