Merge remote-tracking branch 'upstream/libyang2' into libyang2
diff --git a/.travis.yml b/.travis.yml
index ee98aca..ad82c0d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,12 @@
 language: c
 sudo: required
-dist: trusty
+dist: xenial
 branches:
   only:
     - libyang2
+cache:
+   directories:
+   - $HOME/cache
 
 stages:
   - name: Test
@@ -26,12 +29,26 @@
           project:
             name: "CESNET/libyang"
           notification_email: rkrejci@cesnet.cz
-          build_command_prepend: "mkdir build; cd build; cmake ..; make clean"
+          build_command_prepend: "mkdir build && cd build && cmake .. && make clean"
           build_command: "make"
           branch_pattern: libyang2
       before_install:
-        - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt
+        - sudo apt-get install libpcre2-dev
+        # check if something changed from the last coverity build
+        - echo "Last coverity build on revision" `cat $HOME/cache/coveritybuild 2>/dev/null`
+        - echo "Current revision" `git rev-parse HEAD`
+        - if [ "`git rev-parse HEAD`" = "`cat $HOME/cache/coveritybuild`" ]; then echo "Codebase did not change from previous build."; travis_terminate 0; fi
+        - if [ ! -d $HOME/cache ]; then echo "Preparing revision cache."; mkdir -p $HOME/cache; fi
+        - git rev-parse HEAD > $HOME/cache/coveritybuild
+        - cat $HOME/cache/coveritybuild
         - sudo apt-get update -qq
+        - wget https://ftp.pcre.org/pub/pcre/pcre2-10.30.tar.gz
+        - tar -xzf pcre2-10.30.tar.gz
+        - cd pcre2-10.30 && ./configure && make -j2 && sudo -i -- sh -c 'cd /home/travis/build/CESNET/libyang/pcre2-10.30/ && make install' && cd ..
+        # get everything for coverity
+        - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt
+        - wget https://entrust.com/root-certificates/entrust_l1k.cer -O - | sudo tee -a /etc/ssl/certs/ca-certificates.crt
+        - export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
       script:
         # do nothing, everything here is done in coverity addon
         - true
@@ -40,24 +57,30 @@
       os: linux
       compiler: clang
       before_install:
+        - sudo apt-get update -qq && sudo apt-get install -y valgrind
         - wget https://cmocka.org/files/1.1/cmocka-1.1.2.tar.xz
         - tar -xf cmocka-1.1.2.tar.xz
-        - cd cmocka-1.1.2; mkdir build; cd build; cmake .. && make -j2 && sudo make install; cd ../..
-        - sudo apt-get update -qq; sudo apt-get install -y valgrind
+        - cd cmocka-1.1.2 && mkdir build && cd build && cmake .. && make -j2 && sudo make install && cd ../..
+        - wget https://ftp.pcre.org/pub/pcre/pcre2-10.30.tar.gz
+        - tar -xzf pcre2-10.30.tar.gz
+        - cd pcre2-10.30 && ./configure && make -j2 && sudo -i -- sh -c 'cd /home/travis/build/CESNET/libyang/pcre2-10.30/ && make install' && cd ..
       script:
-        - mkdir build && cd build; cmake .. && make -j2 && ctest --output-on-failure; cd -
+        - mkdir build && cd build && cmake .. && make -j2 && ctest --output-on-failure && cd -
     - stage: Test
       name: Linux with GCC
       os: linux
       compiled: gcc
       before_install:
+        - sudo apt-get update -qq && sudo apt-get install -y valgrind
         - wget https://cmocka.org/files/1.1/cmocka-1.1.2.tar.xz
         - tar -xf cmocka-1.1.2.tar.xz
-        - cd cmocka-1.1.2; mkdir build; cd build; cmake .. && make -j2 && sudo make install; cd ../..
-        - sudo apt-get update -qq; sudo apt-get install -y valgrind
-        - pip install --user codecov; export CFLAGS="-coverage"
+        - cd cmocka-1.1.2 && mkdir build && cd build && cmake .. && make -j2 && sudo make install && cd ../..
+        - wget https://ftp.pcre.org/pub/pcre/pcre2-10.30.tar.gz
+        - tar -xzf pcre2-10.30.tar.gz
+        - cd pcre2-10.30 && ./configure && make -j2 && sudo make install && cd ..
+        - pip install --user codecov && export CFLAGS="-coverage"
       script:
-        - mkdir build && cd build; cmake .. && make -j2 && ctest --output-on-failure; cd -
+        - mkdir build && cd build && cmake .. && make -j2 && ctest --output-on-failure && cd -
       after_success:
         - codecov
     - stage: Test
@@ -69,7 +92,9 @@
       before_install:
         - wget https://cmocka.org/files/1.1/cmocka-1.1.2.tar.xz
         - tar -xf cmocka-1.1.2.tar.xz
-        - cd cmocka-1.1.2; mkdir build; cd build; cmake .. && make -j2 && sudo make install; cd ../..
+        - cd cmocka-1.1.2 && mkdir build && cd build && cmake .. && make -j2 && sudo make install && cd ../..
         - brew update
+        - brew install pcre2
       script:
-        - mkdir build && cd build; cmake -DENABLE_VALGRIND_TESTS=OFF .. && make -j2 && ctest --output-on-failure; cd -
+        - mkdir build && cd build && cmake -DENABLE_VALGRIND_TESTS=OFF .. && make -j2 && ctest --output-on-failure && cd -
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d12d7e..346527b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,21 +32,11 @@
     set(CMAKE_BUILD_TYPE debug)
 endif()
 
-set(CMAKE_C_FLAGS         "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -std=c99")
-set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
-set(CMAKE_C_FLAGS_PACKAGE "-g -O2 -DNDEBUG")
-set(CMAKE_C_FLAGS_DEBUG   "-g -O0")
-
 # options
 if((CMAKE_BUILD_TYPE STREQUAL debug) OR (CMAKE_BUILD_TYPE STREQUAL Package))
     option(ENABLE_BUILD_TESTS "Build tests" ON)
     option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" ON)
-
-    if(CMAKE_COMPILER_IS_GNUCC)
-        option(ENABLE_COVERAGE "Build code coverage report from tests" ON)
-    else()
-        option(ENABLE_COVERAGE "Build code coverage report from tests" OFF)
-    endif()
+    option(ENABLE_COVERAGE "Build code coverage report from tests" OFF)
 else()
     option(ENABLE_BUILD_TESTS "Build tests" OFF)
     option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" OFF)
@@ -80,6 +70,44 @@
     set(COMPILER_PACKED_ATTR "")
 endif()
 
+if(ENABLE_COVERAGE)
+    find_program(PATH_GCOV NAMES gcov)
+    if(NOT PATH_GCOV)
+        message(WARNING "'gcov' executable not found! Disabling building code coverage report.")
+        set(ENABLE_COVERAGE OFF)
+    endif()
+
+    find_program(PATH_LCOV NAMES lcov)
+    if(NOT PATH_GCOV)
+        message(WARNING "'lcov' executable not found! Disabling building code coverage report.")
+        set(ENABLE_COVERAGE OFF)
+    endif()
+
+    find_program(PATH_GENHTML NAMES genhtml)
+    if(NOT PATH_GCOV)
+        message(WARNING "'genhtml' executable not found! Disabling building code coverage report.")
+        set(ENABLE_COVERAGE OFF)
+    endif()
+
+    if(NOT CMAKE_COMPILER_IS_GNUCC)
+        message(WARNING "Compiler is not gcc! Coverage may break the tests!")
+    endif()
+
+    if(ENABLE_COVERAGE)
+        set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
+    endif()
+endif()
+
+set(CMAKE_C_FLAGS         "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_COVERAGE} -Wall -Wextra -Wno-missing-field-initializers -std=c99")
+set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
+set(CMAKE_C_FLAGS_PACKAGE "-g -O2 -DNDEBUG")
+set(CMAKE_C_FLAGS_DEBUG   "-g -O0")
+
+list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
+check_symbol_exists(get_current_dir_name unistd.h HAVE_GET_CURRENT_DIR_NAME)
+check_symbol_exists(vdprintf stdio.h HAVE_VDPRINTF)
+check_symbol_exists(strnstr string.h HAVE_STRNSTR)
+
 include_directories(${PROJECT_BINARY_DIR}/src ${PROJECT_SOURCE_DIR}/src)
 configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in ${PROJECT_BINARY_DIR}/src/config.h @ONLY)
 
@@ -161,7 +189,7 @@
 #endif()
 
 # by default build shared library
-# static build requires static libpcre library
+# static build requires static libpcre2 library
 option(ENABLE_STATIC "Build static (.a) library" OFF)
 
 # check the supported platform
@@ -171,47 +199,56 @@
 
 set(libsrc
     src/common.c
+    src/compat.c
     src/log.c
     src/hash_table.c
     src/set.c
     src/context.c
+    src/tree_data.c
+    src/tree_data_free.c
+    src/tree_data_helpers.c
+    src/tree_data_hash.c
+    src/parser_xml.c
+    src/printer_data.c
+    src/printer_xml.c
     src/tree_schema.c
     src/tree_schema_free.c
     src/tree_schema_compile.c
     src/tree_schema_helpers.c
     src/parser_yang.c
     src/parser_yin.c
+    src/printer.c
+    src/printer_schema.c
+    src/printer_yang.c
+    src/plugins_types.c
     src/xml.c
     src/xpath.c)
 
-#set(lintsrc
-#    tools/lint/main.c
-#    tools/lint/main_ni.c
-#    tools/lint/commands.c
-#    tools/lint/completion.c
-#    tools/lint/configuration.c
-#    linenoise/linenoise.c)
+set(lintsrc
+    tools/lint/main.c
+    tools/lint/main_ni.c
+    tools/lint/commands.c
+    tools/lint/completion.c
+    tools/lint/configuration.c
+    tools/lint/linenoise/linenoise.c)
 
-#set(resrc
-#    tools/re/main.c)
-
-#set(yang2yinsrc
-#    tools/yang2yin/main.c)
+set(resrc
+    tools/re/main.c)
 
 set(headers
     src/libyang.h
     src/context.h
+    src/tree.h
+    src/tree_data.h
+    src/printer_data.h
     src/tree_schema.h
+    src/printer_schema.h
     src/extensions.h
+    src/plugins_types.h
     src/dict.h
     src/log.h
     src/set.h)
 
-check_symbol_exists(vdprintf stdio.h HAVE_VDPRINTF)
-if(HAVE_VDPRINTF)
-    add_definitions(-DHAVE_VDPRINTF)
-endif(HAVE_VDPRINTF)
-
 # create static libyang library
 if(ENABLE_STATIC)
     add_definitions(-DSTATIC)
@@ -244,11 +281,11 @@
     target_link_libraries(yang ${CMAKE_THREAD_LIBS_INIT})
 endif(ENABLE_STATIC)
 
-# find PCRE library
-unset(PCRE_LIBRARY CACHE)
-find_package(PCRE REQUIRED)
-include_directories(${PCRE_INCLUDE_DIRS})
-target_link_libraries(yang ${PCRE_LIBRARIES})
+# find PCRE2 library
+unset(PCRE2_LIBRARY CACHE)
+find_package(PCRE2 10.30 REQUIRED)
+include_directories(${PCRE2_INCLUDE_DIRS})
+target_link_libraries(yang ${PCRE2_LIBRARIES})
 
 install(TARGETS yang DESTINATION ${CMAKE_INSTALL_LIBDIR})
 install(FILES ${headers} ${PROJECT_BINARY_DIR}/src/config.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libyang)
@@ -340,19 +377,21 @@
 #
 #configure_file(${PROJECT_SOURCE_DIR}/src/plugin_config.h.in ${PROJECT_BINARY_DIR}/src/plugin_config.h)
 
+# config file for tools
+configure_file(${PROJECT_SOURCE_DIR}/tools/config.h.in ${PROJECT_BINARY_DIR}/tools/config.h @ONLY)
+
 # yanglint
-#add_executable(yanglint ${lintsrc})
-#target_link_libraries(yanglint yang)
-#install(TARGETS yanglint DESTINATION ${CMAKE_INSTALL_BINDIR})
-#install(FILES ${PROJECT_SOURCE_DIR}/tools/lint/yanglint.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+add_executable(yanglint ${lintsrc})
+target_link_libraries(yanglint yang)
+install(TARGETS yanglint DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/tools/lint/yanglint.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+target_include_directories(yanglint BEFORE PRIVATE ${PROJECT_BINARY_DIR}/tools)
 
-#yangre
-#add_executable(yangre ${resrc})
-#target_link_libraries(yangre yang)
-#install(TARGETS yangre DESTINATION ${CMAKE_INSTALL_BINDIR})
-
-# yang2yin
-#add_executable(yang2yin ${yang2yinsrc})
+# yangre
+add_executable(yangre ${resrc})
+target_link_libraries(yangre yang)
+install(TARGETS yangre DESTINATION ${CMAKE_INSTALL_BINDIR})
+target_include_directories(yangre BEFORE PRIVATE ${PROJECT_BINARY_DIR}/tools)
 
 if(ENABLE_VALGRIND_TESTS)
     set(ENABLE_BUILD_TESTS ON)
diff --git a/CMakeModules/FindPCRE.cmake b/CMakeModules/FindPCRE.cmake
deleted file mode 100644
index a28ff11..0000000
--- a/CMakeModules/FindPCRE.cmake
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2007-2009 LuaDist.
-# Created by Peter Kapec <kapecp@gmail.com>
-# Redistribution and use of this file is allowed according to the terms of the MIT license.
-# Note:
-# Searching headers and libraries is very simple and is NOT as powerful as scripts
-# distributed with CMake, because LuaDist defines directories to search for.
-# Everyone is encouraged to contact the author with improvements. Maybe this file
-# becomes part of CMake distribution sometimes.
-
-# - Find pcre
-# Find the native PCRE headers and libraries.
-#
-# PCRE_INCLUDE_DIRS - where to find pcre.h, etc.
-# PCRE_LIBRARIES - List of libraries when using pcre.
-# PCRE_FOUND - True if pcre found.
-
-# Look for the header file.
-FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h)
-
-# Look for the library.
-FIND_LIBRARY(PCRE_LIBRARY NAMES pcre)
-
-# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
-INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR)
-
-# Copy the results to the output variables.
-IF(PCRE_FOUND)
-    SET(PCRE_LIBRARIES ${PCRE_LIBRARY})
-    SET(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
-ELSE(PCRE_FOUND)
-    SET(PCRE_LIBRARIES)
-    SET(PCRE_INCLUDE_DIRS)
-ENDIF(PCRE_FOUND)
-
-MARK_AS_ADVANCED(PCRE_INCLUDE_DIRS PCRE_LIBRARIES)
diff --git a/CMakeModules/FindPCRE2.cmake b/CMakeModules/FindPCRE2.cmake
new file mode 100644
index 0000000..8f60daa
--- /dev/null
+++ b/CMakeModules/FindPCRE2.cmake
@@ -0,0 +1,30 @@
+# - Find pcre
+# Find the native PCRE2 headers and libraries.
+#
+# PCRE2_INCLUDE_DIRS    - where to find pcre.h, etc.
+# PCRE2_LIBRARIES   - List of libraries when using pcre.
+# PCRE2_FOUND   - True if pcre found.
+
+# Look for the header file.
+FIND_PATH(PCRE2_INCLUDE_DIR pcre2.h)
+
+# Look for the library.
+FIND_LIBRARY(PCRE2_LIBRARY NAMES libpcre2.a pcre2-8)
+
+# Check required version
+execute_process(COMMAND pcre2-config --version OUTPUT_VARIABLE PCRE2_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2 REQUIRED_VARS PCRE2_LIBRARY PCRE2_INCLUDE_DIR VERSION_VAR PCRE2_VERSION)
+
+# Copy the results to the output variables.
+IF(PCRE2_FOUND)
+    SET(PCRE2_LIBRARIES ${PCRE2_LIBRARY})
+    SET(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR})
+ELSE(PCRE2_FOUND)
+    SET(PCRE_LIBRARIES)
+    SET(PCRE_INCLUDE_DIRS)
+ENDIF(PCRE2_FOUND)
+
+MARK_AS_ADVANCED(PCRE2_INCLUDE_DIRS PCRE2_LIBRARIES)
\ No newline at end of file
diff --git a/README.md b/README.md
index 410632d..b674eca 100644
--- a/README.md
+++ b/README.md
@@ -39,14 +39,14 @@
 
 * C compiler
 * cmake >= 2.8.12
-* libpcre (devel package)
+* libpcre2 >= 10.30 (including devel package)
  * note, that PCRE is supposed to be compiled with unicode support (configure's options
    `--enable-utf` and `--enable-unicode-properties`)
 
 #### Optional
 
 * doxygen (for generating documentation)
-* cmocka >= 1.0.0 (for [tests](#Tests)
+* cmocka >= 1.0.0 (for [tests](#Tests))
 * valgrind (for enhanced testing)
 * gcov (for code coverage)
 * lcov (for code coverage)
@@ -54,7 +54,7 @@
 
 ### Runtime Requirements
 
-* libpcre
+* libpcre2 >= 10.30
 
 ## Building
 
diff --git a/src/common.c b/src/common.c
index dc96310..bdb671d 100644
--- a/src/common.c
+++ b/src/common.c
@@ -16,16 +16,14 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <limits.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <ctype.h>
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 
+#include "extensions.h"
 #include "tree_schema.h"
 
 const char *const ly_stmt_list[] = {
@@ -145,22 +143,6 @@
     [LYS_DEV_REPLACE] = "replace",
 };
 
-#ifndef  __USE_GNU
-char *
-get_current_dir_name(void)
-{
-    char tmp[PATH_MAX];
-    char *retval;
-
-    if (getcwd(tmp, sizeof(tmp))) {
-        retval = strdup(tmp);
-        LY_CHECK_ERR_RET(!retval, LOGMEM(NULL), NULL);
-        return retval;
-    }
-    return NULL;
-}
-#endif
-
 void *
 ly_realloc(void *ptr, size_t size)
 {
@@ -181,6 +163,10 @@
     int aux;
     int i;
 
+    if (bytes_read) {
+        (*bytes_read) = 0;
+    }
+
     c = (*input)[0];
     LY_CHECK_RET(!c, LY_EINVAL);
 
diff --git a/src/common.h b/src/common.h
index f641526..fb77255 100644
--- a/src/common.h
+++ b/src/common.h
@@ -18,18 +18,19 @@
 #define _DEFAULT_SOURCE
 #define _GNU_SOURCE
 
-#include <assert.h>
 #include <pthread.h>
+#include <stddef.h>
 #include <stdint.h>
-#include <stdlib.h>
-#include <unistd.h>
 
 #include "config.h"
-#include "log.h"
+
 #include "context.h"
-#include "tree_schema.h"
-#include "set.h"
+#include "dict.h"
 #include "hash_table.h"
+#include "log.h"
+#include "set.h"
+
+struct ly_ctx;
 
 #if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
 # define THREAD_LOCAL _Thread_local
@@ -53,13 +54,27 @@
  */
 #define API __attribute__((visibility("default")))
 
-#ifndef  __USE_GNU
-/*
- * If we don't have GNU extension, implement these function on your own
+
+/******************************************************************************
+ * Compatibility functions
+ *****************************************************************************/
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+/**
+ * @brief Return a malloc'd string containing the current directory name.
  */
 char *get_current_dir_name(void);
 #endif
 
+#ifndef HAVE_STRNSTR
+/**
+ * @brief Find the first occurrence of find in s, where the search is limited to the
+ * first slen characters of s.
+ */
+char *strnstr(const char *s, const char *find, size_t slen);
+#endif
+
+
 /******************************************************************************
  * Logger
  *****************************************************************************/
@@ -85,8 +100,32 @@
 extern volatile uint8_t ly_log_level;
 extern volatile uint8_t ly_log_opts;
 
-void ly_err_free(void *ptr);
+/**
+ * @brief Set error-app-tag to the last error record in the context.
+ * @param[in] ctx libyang context where the error records are present.
+ * @param[in] apptag The error-app-tag value to store.
+ */
+void ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag);
+
+/**
+ * @brief Print a log message and store it into the context (if provided).
+ *
+ * @param[in] ctx libyang context to store the error record. If not provided, the error is just printed.
+ * @param[in] level Log message level (error, warning, etc.)
+ * @param[in] no Error type code.
+ * @param[in] format Format string to print.
+ */
 void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...);
+
+/**
+ * @brief Print Validation error and store it into the context (if provided).
+ *
+ * @param[in] ctx libyang context to store the error record. If not provided, the error is just printed.
+ * @param[in] elem_type Type of the data in @p elem variable.
+ * @param[in] elem Object to provide more information about the place where the error appeared.
+ * @param[in] code Validation error code.
+ * @param[in] format Format string to print.
+ */
 void ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...);
 
 #define LOGERR(ctx, errno, str, args...) ly_log(ctx, LY_LLERR, errno, str, ##args)
@@ -112,12 +151,12 @@
 /*
  * Common code to check return value and perform appropriate action.
  */
-#define LY_CHECK_GOTO(COND, GOTO) if (COND) {goto GOTO;}
-#define LY_CHECK_ERR_GOTO(COND, ERR, GOTO) if (COND) {ERR; goto GOTO;}
+#define LY_CHECK_GOTO(COND, GOTO) if ((COND)) {goto GOTO;}
+#define LY_CHECK_ERR_GOTO(COND, ERR, GOTO) if ((COND)) {ERR; goto GOTO;}
 #define LY_CHECK_RET1(RETVAL) {LY_ERR ret__ = RETVAL;if (ret__ != LY_SUCCESS) {return ret__;}}
-#define LY_CHECK_RET2(COND, RETVAL) if (COND) {return RETVAL;}
+#define LY_CHECK_RET2(COND, RETVAL) if ((COND)) {return RETVAL;}
 #define LY_CHECK_RET(...) GETMACRO2(__VA_ARGS__, LY_CHECK_RET2, LY_CHECK_RET1)(__VA_ARGS__)
-#define LY_CHECK_ERR_RET(COND, ERR, RETVAL) if (COND) {ERR; return RETVAL;}
+#define LY_CHECK_ERR_RET(COND, ERR, RETVAL) if ((COND)) {ERR; return RETVAL;}
 
 #define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!(ARG)) {LOGARG(CTX, ARG);goto GOTO;}
 #define LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO) LY_CHECK_ARG_GOTO1(CTX, ARG1, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG2, GOTO)
@@ -136,7 +175,7 @@
 
 #define LY_VCODE_INCHAR      LYVE_SYNTAX, "Invalid character 0x%x."
 #define LY_VCODE_INSTREXP    LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected %s."
-#define LY_VCODE_EOF         LYVE_SYNTAX, "Unexpected end-of-file."
+#define LY_VCODE_EOF         LYVE_SYNTAX, "Unexpected end-of-input."
 #define LY_VCODE_NTERM       LYVE_SYNTAX, "%s not terminated."
 #define LY_VCODE_NSUPP       LYVE_SYNTAX, "%s not supported."
 #define LY_VCODE_INSTMT      LYVE_SYNTAX_YANG, "Invalid keyword \"%s\"."
@@ -453,12 +492,12 @@
  */
 #define LY_ARRAY_CREATE_GOTO(CTX, ARRAY, SIZE, RET, GOTO) \
         if (ARRAY) { \
-            ARRAY = ly_realloc(((uint32_t*)(ARRAY) - 1), sizeof(uint32_t) + ((*((uint32_t*)(ARRAY) - 1) + SIZE) * sizeof *(ARRAY))); \
+            ARRAY = ly_realloc(((uint32_t*)(ARRAY) - 1), sizeof(uint32_t) + ((*((uint32_t*)(ARRAY) - 1) + (SIZE)) * sizeof *(ARRAY))); \
             LY_CHECK_ERR_GOTO(!(ARRAY), LOGMEM(CTX); RET = LY_EMEM, GOTO); \
             ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
-            memset(&(ARRAY)[*((uint32_t*)(ARRAY) - 1)], 0, SIZE * sizeof *(ARRAY)); \
+            memset(&(ARRAY)[*((uint32_t*)(ARRAY) - 1)], 0, (SIZE) * sizeof *(ARRAY)); \
         } else { \
-            ARRAY = calloc(1, sizeof(uint32_t) + SIZE * sizeof *(ARRAY)); \
+            ARRAY = calloc(1, sizeof(uint32_t) + (SIZE) * sizeof *(ARRAY)); \
             LY_CHECK_ERR_GOTO(!(ARRAY), LOGMEM(CTX); RET = LY_EMEM, GOTO); \
             ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
         }
diff --git a/src/compat.c b/src/compat.c
new file mode 100644
index 0000000..c877ceb
--- /dev/null
+++ b/src/compat.c
@@ -0,0 +1,57 @@
+/**
+ * @file compat.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Compatibility functions - implemented basic functions which are not available on all the platforms.
+ *
+ * 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 "common.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#ifndef HAVE_STRNSTR
+char *
+strnstr(const char *s, const char *find, size_t slen)
+{
+    char c, sc;
+    size_t len;
+
+    if ((c = *find++) != '\0') {
+        len = strlen(find);
+        do {
+            do {
+                if (slen-- < 1 || (sc = *s++) == '\0')
+                    return (NULL);
+            } while (sc != c);
+            if (len > slen)
+                return (NULL);
+        } while (strncmp(s, find, len) != 0);
+        s--;
+    }
+    return ((char *)s);
+}
+#endif
+
+#ifndef  HAVE_GET_CURRENT_DIR_NAME
+char *
+get_current_dir_name(void)
+{
+    char tmp[PATH_MAX];
+    char *retval;
+
+    if (getcwd(tmp, sizeof(tmp))) {
+        retval = strdup(tmp);
+        LY_CHECK_ERR_RET(!retval, LOGMEM(NULL), NULL);
+        return retval;
+    }
+    return NULL;
+}
+#endif
diff --git a/src/config.h.in b/src/config.h.in
index be52bdb..a625b8b 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -17,6 +17,12 @@
 
 #cmakedefine APPLE
 
+#cmakedefine HAVE_GET_CURRENT_DIR_NAME
+
+#cmakedefine HAVE_VDPRINTF
+
+#cmakedefine HAVE_STRNSTR
+
 /**
  * @brief Compiler flag for unused function attributes
  */
diff --git a/src/context.c b/src/context.c
index 10542c0..9de0592 100644
--- a/src/context.c
+++ b/src/context.c
@@ -14,17 +14,21 @@
 
 #include "common.h"
 
+#include <assert.h>
 #include <errno.h>
-#include <limits.h>
+#include <pthread.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 #include "context.h"
+#include "hash_table.h"
+#include "set.h"
+#include "tree.h"
 #include "tree_schema_internal.h"
-#include "libyang.h"
+#include "plugins_types.h"
 
 #define LY_INTERNAL_MODS_COUNT 6
 
@@ -62,19 +66,22 @@
     LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
 
     if (search_dir) {
-        LY_CHECK_ERR_RET(access(search_dir, R_OK | X_OK),
-                         LOGERR(ctx, LY_ESYS, "Unable to use search directory \"%s\" (%s)", search_dir, strerror(errno)),
-                         LY_EINVAL);
-        LY_CHECK_ERR_RET(stat(search_dir, &st),
-                         LOGERR(ctx, LY_ESYS, "stat() failed for \"%s\" (%s)", search_dir, strerror(errno)),
-                         LY_ESYS);
-        LY_CHECK_ERR_RET(!S_ISDIR(st.st_mode),
-                         LOGERR(ctx, LY_ESYS, "Given search directory \"%s\" is not a directory.", search_dir),
-                         LY_EINVAL);
         new_dir = realpath(search_dir, NULL);
         LY_CHECK_ERR_RET(!new_dir,
-                         LOGERR(ctx, LY_ESYS, "realpath() call failed for \"%s\" (%s).", search_dir, strerror(errno)),
+                         LOGERR(ctx, LY_ESYS, "Unable to use search directory \"%s\" (%s).", search_dir, strerror(errno)),
+                         LY_EINVAL);
+        if (strcmp(search_dir, new_dir)) {
+            LOGVRB("Canonicalizing search directory string from \"%s\" to \"%s\".", search_dir, new_dir);
+        }
+        LY_CHECK_ERR_RET(access(new_dir, R_OK | X_OK),
+                         LOGERR(ctx, LY_ESYS, "Unable to fully access search directory \"%s\" (%s).", new_dir, strerror(errno)); free(new_dir),
+                         LY_EINVAL);
+        LY_CHECK_ERR_RET(stat(new_dir, &st),
+                         LOGERR(ctx, LY_ESYS, "stat() failed for \"%s\" (%s).", new_dir, strerror(errno)); free(new_dir),
                          LY_ESYS);
+        LY_CHECK_ERR_RET(!S_ISDIR(st.st_mode),
+                         LOGERR(ctx, LY_ESYS, "Given search directory \"%s\" is not a directory.", new_dir); free(new_dir),
+                         LY_EINVAL);
         /* avoid path duplication */
         for (u = 0; u < ctx->search_paths.count; ++u) {
             if (!strcmp(new_dir, ctx->search_paths.objs[u])) {
@@ -151,6 +158,36 @@
 }
 
 API LY_ERR
+ly_ctx_unset_searchdir(struct ly_ctx *ctx, unsigned int index)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
+
+    if (!ctx->search_paths.count) {
+        return LY_SUCCESS;
+    }
+
+    if (index >= ctx->search_paths.count) {
+        LOGARG(ctx, value);
+        return LY_EINVAL;
+    } else {
+        return ly_set_rm_index(&ctx->search_paths, index, free);
+    }
+
+    return LY_SUCCESS;
+}
+
+API const struct lys_module *
+ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision)
+{
+    struct lys_module *result = NULL;
+
+    LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+
+    LY_CHECK_RET(lysp_load_module(ctx, name, revision, 1, 0, &result), NULL);
+    return result;
+}
+
+API LY_ERR
 ly_ctx_new(const char *search_dir, int options, struct ly_ctx **new_ctx)
 {
     struct ly_ctx *ctx = NULL;
@@ -160,6 +197,8 @@
     int i;
     LY_ERR rc = LY_SUCCESS;
 
+    LY_CHECK_ARG_RET(NULL, new_ctx, LY_EINVAL);
+
     ctx = calloc(1, sizeof *ctx);
     LY_CHECK_ERR_RET(!ctx, LOGMEM(NULL), LY_EMEM);
 
@@ -277,6 +316,18 @@
     return ctx->imp_clb;
 }
 
+API const struct lys_module *
+ly_ctx_get_module_iter(const struct ly_ctx *ctx, unsigned int *index)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, index, NULL);
+
+    if (*index < (unsigned)ctx->list.count) {
+        return ctx->list.objs[(*index)++];
+    } else {
+        return NULL;
+    }
+}
+
 /**
  * @brief Iterate over the modules in the given context. Returned modules must match the given key at the offset of
  * lysp_module and lysc_module structures (they are supposed to be placed at the same offset in both structures).
diff --git a/src/context.h b/src/context.h
index e920cb0..cbe027f 100644
--- a/src/context.h
+++ b/src/context.h
@@ -15,6 +15,8 @@
 #ifndef LY_CONTEXT_H_
 #define LY_CONTEXT_H_
 
+#include <stdint.h>
+
 #include "log.h"
 #include "tree_schema.h"
 
@@ -104,6 +106,8 @@
 /**
  * @brief Clean the search path(s) from the libyang context
  *
+ * To remove the search path by its index, use ly_ctx_unset_searchdir().
+ *
  * @param[in] ctx Context to be modified.
  * @param[in] value Searchdir to be removed, use NULL to remove them all.
  * @return LY_ERR return value
@@ -111,6 +115,17 @@
 LY_ERR ly_ctx_unset_searchdirs(struct ly_ctx *ctx, const char *value);
 
 /**
+ * @brief Remove the specific search path from the libyang context.
+ *
+ * To remove the search path by its value, use ly_ctx_unset_searchdirs().
+ *
+ * @param[in] ctx Context to be modified.
+ * @param[in] index Index of the searchdir to be removed.
+ * @return LY_ERR return value
+ */
+LY_ERR ly_ctx_unset_searchdir(struct ly_ctx *ctx, unsigned int index);
+
+/**
  * @brief Get the NULL-terminated list of the search paths in libyang context. Do not modify the result!
  *
  * @param[in] ctx Context to query.
@@ -232,6 +247,25 @@
 struct lys_module *ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name);
 
 /**
+ * @brief Get the (only) implemented YANG module specified by its name.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The only implemented YANG module revision of the given name in the given context. NULL if there is no
+ * implemented module of the given name.
+ */
+/**
+ * @brief Iterate over all modules in the given context.
+ *
+ * @param[in] ctx Context with the modules.
+ * @param[in,out] index Index of the next module to get. Value of 0 starts from the beginning.
+ * The value is updated with each call, so to iterate over all modules the same variable is supposed
+ * to be used in all calls starting with value 0.
+ * @return Next context module, NULL if the last was already returned.
+ */
+const struct lys_module *ly_ctx_get_module_iter(const struct ly_ctx *ctx, unsigned int *index);
+
+/**
  * @brief Get YANG module of the given namespace and revision.
  *
  * @param[in] ctx Context to work in.
@@ -293,6 +327,23 @@
 LY_ERR ly_ctx_module_implement(struct ly_ctx *ctx, struct lys_module *mod);
 
 /**
+ * @brief Try to find the model in the searchpaths of \p ctx and load it into it. If custom missing
+ * module callback is set, it is used instead.
+ *
+ * The context itself is searched for the requested module first. If \p revision is not specified
+ * (the module of the latest revision is requested) and there is implemented revision of the requested
+ * module in the context, this implemented revision is returned despite there might be a newer revision.
+ * This behavior is cause by the fact that it is not possible to have multiple implemented revisions of
+ * the same module in the context.
+ *
+ * @param[in] ctx Context to add to.
+ * @param[in] name Name of the module to load.
+ * @param[in] revision Optional revision date of the module. If not specified, the latest revision is loaded.
+ * @return Pointer to the data model structure, NULL if not found or some error occurred.
+ */
+const struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision);
+
+/**
  * @brief Free all internal structures of the specified context.
  *
  * The function should be used before terminating the application to destroy
diff --git a/src/hash_table.c b/src/hash_table.c
index 11ca8f3..f26d04c 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -12,14 +12,14 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
+#include "common.h"
+
 #include <string.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <pthread.h>
 #include <assert.h>
 
-#include "common.h"
-#include "context.h"
 #include "hash_table.h"
 
 static int
@@ -244,7 +244,7 @@
     return result;
 }
 
-static struct ht_rec *
+struct ht_rec *
 lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
 {
     return (struct ht_rec *)&recs[idx * rec_size];
diff --git a/src/hash_table.h b/src/hash_table.h
index 70ec695..ac42631 100644
--- a/src/hash_table.h
+++ b/src/hash_table.h
@@ -16,11 +16,13 @@
 #ifndef LY_HASH_TABLE_H_
 #define LY_HASH_TABLE_H_
 
-#include <stdint.h>
-#include <pthread.h>
-
 #include "common.h"
-#include "dict.h"
+
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "log.h"
 
 /**
  * @brief Compute hash from (several) string(s).
diff --git a/src/libyang.h b/src/libyang.h
index 3c001fc..a702e0b 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -25,7 +25,11 @@
 #include "set.h"
 #include "dict.h"
 #include "context.h"
+#include "tree.h"
+#include "tree_data.h"
 #include "tree_schema.h"
+#include "printer_schema.h"
+#include "printer_data.h"
 
 /**
  * @mainpage About
diff --git a/src/log.c b/src/log.c
index b576437..b600b62 100644
--- a/src/log.c
+++ b/src/log.c
@@ -16,11 +16,14 @@
 
 #include <assert.h>
 #include <inttypes.h>
+#include <pthread.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include "libyang.h"
-#include "context.h"
+#include "log.h"
 
 THREAD_LOCAL enum int_log_opts log_opt;
 volatile uint8_t ly_log_level = LY_LLWRN;
@@ -106,6 +109,27 @@
 }
 
 API struct ly_err_item *
+ly_err_new(LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
+{
+    struct ly_err_item *eitem;
+
+    eitem = malloc(sizeof *eitem);
+    LY_CHECK_ERR_RET(!eitem, LOGMEM(NULL), NULL);
+    eitem->prev = eitem;
+    eitem->next = NULL;
+
+    /* fill in the information */
+    eitem->level = level;
+    eitem->no = no;
+    eitem->vecode = vecode;
+    eitem->msg = msg;
+    eitem->path = path;
+    eitem->apptag = apptag;
+
+    return eitem;
+}
+
+API struct ly_err_item *
 ly_err_first(const struct ly_ctx *ctx)
 {
     LY_CHECK_ARG_RET(NULL, ctx, NULL);
@@ -113,7 +137,7 @@
     return pthread_getspecific(ctx->errlist_key);
 }
 
-void
+API void
 ly_err_free(void *ptr)
 {
     struct ly_err_item *i, *next;
@@ -454,3 +478,16 @@
     }
 }
 
+void
+ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag)
+{
+    struct ly_err_item *i;
+
+    if (log_opt != ILO_IGNORE) {
+        i = ly_err_first(ctx);
+        if (i) {
+            i = i->prev;
+            i->apptag = strdup(apptag);
+        }
+    }
+}
diff --git a/src/log.h b/src/log.h
index 89a51a8..c5f8216 100644
--- a/src/log.h
+++ b/src/log.h
@@ -150,7 +150,8 @@
     LY_EINT,        /**< Internal error */
     LY_EVALID,      /**< Validation failure */
     LY_EPLUGIN,     /**< Error reported by a plugin */
-    LY_EDENIED      /**< Operation is not allowed */
+    LY_EDENIED,     /**< Operation is not allowed */
+    LY_EOTHER       /**< Unknown error */
 } LY_ERR;
 
 /**
@@ -166,7 +167,9 @@
     LYVE_SYNTAX_YIN,   /**< YIN-related syntax error */
     LYVE_REFERENCE,    /**< invalid referencing or using an item */
     LYVE_XPATH,        /**< invalid XPath expression */
-    LYVE_SEMANTICS     /**< generic semantic error */
+    LYVE_SEMANTICS,    /**< generic semantic error */
+    LYVE_SYNTAX_XML,   /**< XML-related syntax error */
+    LYVE_RESTRICTION   /**< YANG data does not reflect some of the module restrictions */
 } LY_VECODE;
 
 /**
diff --git a/src/parser_xml.c b/src/parser_xml.c
new file mode 100644
index 0000000..3d86181
--- /dev/null
+++ b/src/parser_xml.c
@@ -0,0 +1,282 @@
+/**
+ * @file parser_xml.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief XML data parser for libyang
+ *
+ * Copyright (c) 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
+ */
+
+#include "common.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "context.h"
+#include "dict.h"
+#include "log.h"
+#include "plugins_types.h"
+#include "set.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "xml.h"
+
+/**
+ * @brief internal context for XML YANG data parser.
+ *
+ * The leading part is compatible with the struct lyxml_context
+ */
+struct lyd_xml_ctx {
+    struct ly_ctx *ctx;              /**< libyang context */
+    uint64_t line;                   /**< number of the line being currently processed */
+    enum LYXML_PARSER_STATUS status; /**< status providing information about the next expected object in input data */
+    struct ly_set elements;          /**< list of not-yet-closed elements */
+    struct ly_set ns;                /**< handled with LY_SET_OPT_USEASLIST */
+
+    uint16_t options;                /**< various @ref dataparseroptions. */
+    uint16_t path_len;               /**< used bytes in the path buffer */
+#define LYD_PARSER_BUFSIZE 4078
+    char path[LYD_PARSER_BUFSIZE];   /**< buffer for the generated path */
+};
+
+/**
+ * @brief Parse XML attributes of the XML element of YANG data.
+ *
+ * @param[in] ctx XML YANG data parser context.
+ * @param[in,out] data Pointer to the XML string representation of the YANG data to parse.
+ * @param[out] attributes Resulting list of the parsed attributes. XML namespace definitions are not parsed
+ * as attributes, they are stored internally in the parser context.
+ * @reutn LY_ERR value.
+ */
+static LY_ERR
+lydxml_attributes(struct lyd_xml_ctx *ctx, const char **data, struct lyd_attr **attributes)
+{
+    LY_ERR ret = LY_SUCCESS;
+    unsigned int u;
+    const char *prefix, *name;
+    size_t prefix_len, name_len;
+    struct lyd_attr *attr = NULL, *last = NULL;
+    const struct lyxml_ns *ns;
+    struct ly_set attr_prefixes = {0};
+    struct attr_prefix_s {
+        const char *prefix;
+        size_t prefix_len;
+    } *attr_prefix;
+    struct lys_module *mod;
+
+    while(ctx->status == LYXML_ATTRIBUTE &&
+            lyxml_get_attribute((struct lyxml_context*)ctx, data, &prefix, &prefix_len, &name, &name_len) == LY_SUCCESS) {
+        int dynamic = 0;
+        char *buffer = NULL, *value;
+        size_t buffer_size = 0, value_len;
+
+        if (!name) {
+            /* seems like all the attrributes were internally processed as namespace definitions */
+            continue;
+        }
+
+        /* get attribute value */
+        ret = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        attr = calloc(1, sizeof *attr);
+        LY_CHECK_ERR_GOTO(!attr, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
+
+        attr->name = lydict_insert(ctx->ctx, name, name_len);
+        /* auxiliary store the prefix information and wait with resolving prefix to the time when all the namespaces,
+         * defined in this element, are parsed, so we will get the correct namespace for this prefix */
+        attr_prefix = malloc(sizeof *attr_prefix);
+        attr_prefix->prefix = prefix;
+        attr_prefix->prefix_len = prefix_len;
+        ly_set_add(&attr_prefixes, attr_prefix, LY_SET_OPT_USEASLIST);
+
+        /* TODO process value */
+
+        if (last) {
+            last->next = attr;
+        } else {
+            (*attributes) = attr;
+        }
+        last = attr;
+    }
+
+    /* resolve annotation pointers in all the attributes */
+    for (last = *attributes, u = 0; u < attr_prefixes.count && last; u++, last = last->next) {
+        attr_prefix = (struct attr_prefix_s*)attr_prefixes.objs[u];
+        ns = lyxml_ns_get((struct lyxml_context *)ctx, attr_prefix->prefix, attr_prefix->prefix_len);
+        mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
+
+        /* TODO get annotation */
+    }
+
+cleanup:
+
+    ly_set_erase(&attr_prefixes, free);
+    return ret;
+}
+
+/**
+ * @brief Parse XML elements as children YANG data node of the specified parent node.
+ *
+ * @param[in] ctx XML YANG data parser context.
+ * @param[in] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
+ * @param[in,out] data Pointer to the XML string representation of the YANG data to parse.
+ * @param[out] node Resulting list of the parsed nodes.
+ * @reutn LY_ERR value.
+ */
+static LY_ERR
+lydxml_nodes(struct lyd_xml_ctx *ctx, struct lyd_node_inner *parent, const char **data, struct lyd_node **node)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *prefix, *name;
+    size_t prefix_len, name_len;
+    struct lyd_attr *attributes = NULL;
+    const struct lyxml_ns *ns;
+    const struct lysc_node *snode;
+    struct lys_module *mod;
+    unsigned int parents_count = ctx->elements.count;
+    struct lyd_node *cur = NULL, *prev = NULL;
+
+    (*node) = NULL;
+
+    while(ctx->status == LYXML_ELEMENT) {
+        ret = lyxml_get_element((struct lyxml_context *)ctx, data, &prefix, &prefix_len, &name, &name_len);
+        LY_CHECK_GOTO(ret, cleanup);
+        if (!name) {
+            /* closing previous element */
+            if (ctx->elements.count < parents_count) {
+                /* all siblings parsed */
+                break;
+            } else {
+                continue;
+            }
+        }
+        attributes = NULL;
+        LY_CHECK_GOTO(lydxml_attributes(ctx, data, &attributes), cleanup);
+        ns = lyxml_ns_get((struct lyxml_context *)ctx, prefix, prefix_len);
+        if (!ns) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%*.s\".", prefix_len, prefix);
+            goto cleanup;
+        }
+        mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
+        if (!mod) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
+            goto cleanup;
+        }
+        snode = lys_child(parent ? parent->schema : NULL, mod, name, name_len, 0, (ctx->options & LYD_OPT_RPCREPLY) ? LYS_GETNEXT_OUTPUT : 0);
+        if (!snode) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Element \"%.*s\" not found in the \"%s\" module.", name_len, name, mod->name);
+            goto cleanup;
+        }
+
+        /* allocate new node */
+        switch (snode->nodetype) {
+        case LYS_CONTAINER:
+        case LYS_LIST:
+            cur = calloc(1, sizeof(struct lyd_node_inner));
+            break;
+        case LYS_LEAF:
+        case LYS_LEAFLIST:
+            cur = calloc(1, sizeof(struct lyd_node_term));
+            break;
+        case LYS_ANYDATA:
+        case LYS_ANYXML:
+            cur = calloc(1, sizeof(struct lyd_node_any));
+            break;
+        /* TODO LYS_ACTION, LYS_NOTIF */
+        default:
+            LOGINT(ctx->ctx);
+            goto cleanup;
+        }
+        if (!(*node)) {
+            (*node) = cur;
+        }
+        cur->schema = snode;
+        cur->parent = parent;
+        if (parent) {
+            parent->child->prev = cur;
+        } else if (prev) {
+            struct lyd_node *iter;
+            for (iter = prev; iter->prev->next; iter = iter->prev);
+            iter->prev = cur;
+        }
+        if (prev) {
+            cur->prev = prev;
+            prev->next = cur;
+        } else {
+            cur->prev = cur;
+        }
+        prev = cur;
+        cur->attr = attributes;
+        attributes = NULL;
+
+        if (snode->nodetype & LYD_NODE_TERM) {
+            int dynamic = 0;
+            char *buffer = NULL, *value;
+            size_t buffer_size = 0, value_len;
+
+            if (ctx->status == LYXML_ELEM_CONTENT) {
+                /* get the value */
+                LY_ERR r = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
+                if (r == LY_EINVAL) {
+                    /* just indentation of a child element found */
+                    LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX, "Child element inside terminal node \"%s\" found.", cur->schema->name);
+                    goto cleanup;
+                }
+            } else {
+                /* no content - validate empty value */
+                value = "";
+                value_len = 0;
+            }
+            LY_CHECK_ERR_GOTO(ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic),
+                              if (dynamic){free(value);}, cleanup);
+        } else if (snode->nodetype & LYD_NODE_INNER) {
+            int dynamic = 0;
+            char *buffer = NULL, *value;
+            size_t buffer_size = 0, value_len;
+
+            if (ctx->status == LYXML_ELEM_CONTENT) {
+                LY_ERR r = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
+                if (r != LY_EINVAL) {
+                    LOGINT(ctx->ctx);
+                    goto cleanup;
+                }
+            }
+            if (ctx->status == LYXML_ELEMENT) {
+                ret = lydxml_nodes(ctx, (struct lyd_node_inner*)cur, data, lyd_node_children_p(cur));
+                LY_CHECK_GOTO(ret, cleanup);
+            }
+        }
+        /* TODO anyxml/anydata */
+    }
+
+cleanup:
+    lyd_free_attr(ctx->ctx, attributes, 1);
+    return ret;
+}
+
+LY_ERR
+lyd_parse_xml(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **result)
+{
+    LY_ERR ret;
+    struct lyd_xml_ctx xmlctx = {0};
+
+    xmlctx.options = options;
+    xmlctx.ctx = ctx;
+    xmlctx.line = 1;
+
+    ret = lydxml_nodes(&xmlctx, NULL, &data, result);
+    if (ret) {
+        lyd_free_all(*result);
+        *result = NULL;
+    }
+    lyxml_context_clear((struct lyxml_context*)&xmlctx);
+    return ret;
+}
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 18994e4..bf1361c 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -11,22 +11,24 @@
  *
  *     https://opensource.org/licenses/BSD-3-Clause
  */
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <errno.h>
-#include <ctype.h>
-#include <string.h>
-#include <dirent.h>
-#include <assert.h>
 
 #include "common.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include "context.h"
-#include "libyang.h"
+#include "dict.h"
+#include "extensions.h"
+#include "log.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_schema.h"
 #include "tree_schema_internal.h"
 
 /* Macro to check YANG's yang-char grammar rule */
@@ -117,12 +119,12 @@
 #define YANG_CHECK_STMTVER2_RET(CTX, KW, PARENT) \
     if ((CTX)->mod_version < 2) {LOGVAL_YANG((CTX), LY_VCODE_INCHILDSTMT2, KW, PARENT); return LY_EVALID;}
 
-static LY_ERR parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
-static LY_ERR parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
-static LY_ERR parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
-static LY_ERR parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
-static LY_ERR parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
-static LY_ERR parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings);
+LY_ERR parse_container(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_uses(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_choice(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_case(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_list(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_grouping(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings);
 
 /**
  * @brief Add another character to dynamic buffer, a low-level function.
@@ -138,7 +140,7 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
+LY_ERR
 buf_add_char(struct ly_ctx *ctx, const char **input, size_t len, char **buf, size_t *buf_len, size_t *buf_used)
 {
     if (*buf_len <= (*buf_used) + len) {
@@ -146,7 +148,11 @@
         *buf = ly_realloc(*buf, *buf_len);
         LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
     }
-    memcpy(&(*buf)[*buf_used], *input, len);
+    if (*buf_used) {
+        memcpy(&(*buf)[*buf_used], *input, len);
+    } else {
+        memcpy(*buf, *input, len);
+    }
 
     (*buf_used) += len;
     (*input) += len;
@@ -162,7 +168,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-check_stringchar(struct ly_parser_ctx *ctx, unsigned int c)
+check_stringchar(struct lys_parser_ctx *ctx, unsigned int c)
 {
     if (!is_yangutf8char(c)) {
         LOGVAL_YANG(ctx, LY_VCODE_INCHAR, c);
@@ -185,8 +191,8 @@
  * If the identifier cannot be prefixed, NULL is expected.
  * @return LY_ERR values.
  */
-static LY_ERR
-check_identifierchar(struct ly_parser_ctx *ctx, unsigned int c, int first, int *prefix)
+LY_ERR
+check_identifierchar(struct lys_parser_ctx *ctx, unsigned int c, int first, int *prefix)
 {
     if (first || (prefix && (*prefix) == 1)) {
         if (!is_yangidentstartchar(c)) {
@@ -226,14 +232,17 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-buf_store_char(struct ly_parser_ctx *ctx, const char **input, enum yang_arg arg,
+LY_ERR
+buf_store_char(struct lys_parser_ctx *ctx, const char **input, enum yang_arg arg,
                char **word_p, size_t *word_len, char **word_b, size_t *buf_len, int need_buf)
 {
     int prefix = 0;
     unsigned int c;
     size_t len;
 
+    /* check  valid combination of input paremeters - if need_buf specified, word_b must be provided */
+    assert(!need_buf || (need_buf && word_b));
+
     /* get UTF8 code point (and number of bytes coding the character) */
     LY_CHECK_ERR_RET(ly_getutf8(input, &c, &len),
                      LOGVAL_YANG(ctx, LY_VCODE_INCHAR, (*input)[-len]), LY_EVALID);
@@ -267,7 +276,7 @@
 
         /* in case of realloc */
         *word_p = *word_b;
-    } else if (need_buf) {
+    } else if (word_b && need_buf) {
         /* first time we need a buffer, copy everything read up to now */
         if (*word_len) {
             *word_b = malloc(*word_len);
@@ -307,8 +316,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-skip_comment(struct ly_parser_ctx *ctx, const char **data, int comment)
+LY_ERR
+skip_comment(struct lys_parser_ctx *ctx, const char **data, int comment)
 {
     /* internal statuses: 0 - comment ended,
      *                    1 - in line comment,
@@ -353,7 +362,7 @@
     }
 
     if (!**data && (comment > 1)) {
-        LOGVAL_YANG(ctx, LYVE_SYNTAX, "Unexpected end-of-file, non-terminated comment.");
+        LOGVAL_YANG(ctx, LYVE_SYNTAX, "Unexpected end-of-input, non-terminated comment.");
         return LY_EVALID;
     }
 
@@ -377,7 +386,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-read_qstring(struct ly_parser_ctx *ctx, const char **data, enum yang_arg arg, char **word_p, char **word_b, size_t *word_len,
+read_qstring(struct lys_parser_ctx *ctx, const char **data, enum yang_arg arg, char **word_p, char **word_b, size_t *word_len,
              size_t *buf_len)
 {
     /* string: 0 - string ended, 1 - string with ', 2 - string with ", 3 - string with " with last character \,
@@ -555,6 +564,11 @@
     }
 
 string_end:
+    if (arg <= Y_PREF_IDENTIF_ARG && !(*word_len)) {
+        /* empty identifier */
+        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Statement argument is required.");
+        return LY_EVALID;
+    }
     return LY_SUCCESS;
 }
 
@@ -564,6 +578,7 @@
  * @param[in] ctx yang parser context for logging.
  * @param[in,out] data Data to read from, always moved to currently handled character.
  * @param[in] arg Type of YANG keyword argument expected.
+ * @param[out] flags optional output argument to get flag of the argument's quoting (LYS_*QOUTED - see [schema node flags](@ref snodeflags))
  * @param[out] word_p Pointer to the read string. Can return NULL if \p arg is #Y_MAYBE_STR_ARG.
  * @param[out] word_b Pointer to a dynamically-allocated buffer holding the read string. If not needed,
  * set to NULL. Otherwise equal to \p word_p.
@@ -571,8 +586,9 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-get_argument(struct ly_parser_ctx *ctx, const char **data, enum yang_arg arg, char **word_p, char **word_b, size_t *word_len)
+LY_ERR
+get_argument(struct lys_parser_ctx *ctx, const char **data, enum yang_arg arg,
+             uint16_t *flags, char **word_p, char **word_b, size_t *word_len)
 {
     size_t buf_len = 0;
 
@@ -593,6 +609,9 @@
                             "unquoted string character, optsep, semicolon or opening brace");
                 return LY_EVALID;
             }
+            if (flags) {
+                (*flags) |= (**data) == '\'' ? LYS_SINGLEQUOTED : LYS_DOUBLEQUOTED;
+            }
             LY_CHECK_RET(read_qstring(ctx, data, arg, word_p, word_b, word_len, &buf_len));
             goto str_end;
         case '/':
@@ -659,6 +678,10 @@
         }
     }
 
+    /* unexpected end of loop */
+    LOGVAL_YANG(ctx, LY_VCODE_EOF);
+    return LY_EVALID;
+
 str_end:
     /* terminating NULL byte for buf */
     if (*word_b) {
@@ -682,8 +705,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-get_keyword(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword *kw, char **word_p, size_t *word_len)
+LY_ERR
+get_keyword(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword *kw, char **word_p, size_t *word_len)
 {
     int prefix;
     const char *word_start;
@@ -999,7 +1022,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_ext_substmt(struct ly_parser_ctx *ctx, const char **data, char *word, size_t word_len,
+parse_ext_substmt(struct lys_parser_ctx *ctx, const char **data, char *word, size_t word_len,
                   struct lysp_stmt **child)
 {
     char *buf;
@@ -1010,10 +1033,18 @@
     stmt = calloc(1, sizeof *stmt);
     LY_CHECK_ERR_RET(!stmt, LOGMEM(NULL), LY_EMEM);
 
+    /* insert into parent statements */
+    if (!*child) {
+        *child = stmt;
+    } else {
+        for (par_child = *child; par_child->next; par_child = par_child->next);
+        par_child->next = stmt;
+    }
+
     stmt->stmt = lydict_insert(ctx->ctx, word, word_len);
 
     /* get optional argument */
-    LY_CHECK_RET(get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_MAYBE_STR_ARG, &stmt->flags, &word, &buf, &word_len));
 
     if (word) {
         if (buf) {
@@ -1023,14 +1054,6 @@
         }
     }
 
-    /* insert into parent statements */
-    if (!*child) {
-        *child = stmt;
-    } else {
-        for (par_child = *child; par_child->next; par_child = par_child->next);
-        par_child->next = stmt;
-    }
-
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, ) {
         LY_CHECK_RET(parse_ext_substmt(ctx, data, word, word_len, &stmt->child));
     }
@@ -1051,7 +1074,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_ext(struct ly_parser_ctx *ctx, const char **data, const char *ext_name, int ext_name_len, LYEXT_SUBSTMT insubstmt,
+parse_ext(struct lys_parser_ctx *ctx, const char **data, const char *ext_name, int ext_name_len, LYEXT_SUBSTMT insubstmt,
           uint32_t insubstmt_index, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -1068,7 +1091,7 @@
     e->insubstmt_index = insubstmt_index;
 
     /* get optional argument */
-    LY_CHECK_RET(get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_MAYBE_STR_ARG, NULL, &word, &buf, &word_len));
 
     if (word) {
         INSERT_WORD(ctx, buf, e->argument, word, word_len);
@@ -1095,7 +1118,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_text_field(struct ly_parser_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, uint32_t substmt_index,
+parse_text_field(struct lys_parser_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, uint32_t substmt_index,
                  const char **value, enum yang_arg arg, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -1109,7 +1132,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, arg, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, arg, NULL, &word, &buf, &word_len));
 
     /* store value and spend buf if allocated */
     INSERT_WORD(ctx, buf, *value, word, word_len);
@@ -1138,7 +1161,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_yangversion(struct ly_parser_ctx *ctx, const char **data, uint8_t *version, struct lysp_ext_instance **exts)
+parse_yangversion(struct lys_parser_ctx *ctx, const char **data, uint8_t *version, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1151,7 +1174,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len == 3) && !strncmp(word, "1.0", word_len)) {
         *version = LYS_VERSION_1_0;
@@ -1189,7 +1212,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_belongsto(struct ly_parser_ctx *ctx, const char **data, const char **belongsto, const char **prefix, struct lysp_ext_instance **exts)
+parse_belongsto(struct lys_parser_ctx *ctx, const char **data, const char **belongsto, const char **prefix, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1202,7 +1225,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
 
     INSERT_WORD(ctx, buf, *belongsto, word, word_len);
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
@@ -1239,7 +1262,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_revisiondate(struct ly_parser_ctx *ctx, const char **data, char *rev, struct lysp_ext_instance **exts)
+parse_revisiondate(struct lys_parser_ctx *ctx, const char **data, char *rev, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1252,7 +1275,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     /* check value */
     if (lysp_check_date(ctx, word, word_len, "revision-date")) {
@@ -1288,7 +1311,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_include(struct ly_parser_ctx *ctx, const char *module_name, const char **data, struct lysp_include **includes)
+parse_include(struct lys_parser_ctx *ctx, const char *module_name, const char **data, struct lysp_include **includes)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1299,7 +1322,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *includes, inc, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
 
     INSERT_WORD(ctx, buf, inc->name, word, word_len);
 
@@ -1345,7 +1368,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_import(struct ly_parser_ctx *ctx, const char *module_prefix, const char **data, struct lysp_import **imports)
+parse_import(struct lys_parser_ctx *ctx, const char *module_prefix, const char **data, struct lysp_import **imports)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1356,14 +1379,14 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *imports, imp, LY_EVALID);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, imp->name, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_PREFIX:
             LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &imp->prefix, Y_IDENTIF_ARG, &imp->exts));
-            LY_CHECK_RET(lysp_check_prefix(ctx->ctx, &ctx->line, *imports, module_prefix, &imp->prefix), LY_EVALID);
+            LY_CHECK_RET(lysp_check_prefix(ctx, *imports, module_prefix, &imp->prefix), LY_EVALID);
             break;
         case YANG_DESCRIPTION:
             YANG_CHECK_STMTVER2_RET(ctx, "description", "import");
@@ -1402,7 +1425,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_revision(struct ly_parser_ctx *ctx, const char **data, struct lysp_revision **revs)
+parse_revision(struct lys_parser_ctx *ctx, const char **data, struct lysp_revision **revs)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1413,7 +1436,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *revs, rev, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     /* check value */
     if (lysp_check_date(ctx, word, word_len, "revision")) {
@@ -1455,7 +1478,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_text_fields(struct ly_parser_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, const char ***texts, enum yang_arg arg,
+parse_text_fields(struct lys_parser_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, const char ***texts, enum yang_arg arg,
                   struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -1468,7 +1491,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *texts, item, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, arg, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, arg, NULL, &word, &buf, &word_len));
 
     INSERT_WORD(ctx, buf, *item, word, word_len);
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -1495,7 +1518,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_config(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+parse_config(struct lys_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1508,7 +1531,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *flags |= LYS_CONFIG_W;
@@ -1545,7 +1568,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_mandatory(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+parse_mandatory(struct lys_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1558,7 +1581,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *flags |= LYS_MAND_TRUE;
@@ -1595,7 +1618,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_restr(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword restr_kw, struct lysp_restr *restr)
+parse_restr(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword restr_kw, struct lysp_restr *restr)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1603,7 +1626,7 @@
     enum yang_keyword kw;
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     INSERT_WORD(ctx, buf, restr->arg, word, word_len);
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -1642,7 +1665,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_restrs(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword restr_kw, struct lysp_restr **restrs)
+parse_restrs(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword restr_kw, struct lysp_restr **restrs)
 {
     struct lysp_restr *restr;
 
@@ -1661,7 +1684,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_status(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+parse_status(struct lys_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1674,7 +1697,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len == 7) && !strncmp(word, "current", word_len)) {
         *flags |= LYS_STATUS_CURR;
@@ -1712,7 +1735,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_when(struct ly_parser_ctx *ctx, const char **data, struct lysp_when **when_p)
+parse_when(struct lys_parser_ctx *ctx, const char **data, struct lysp_when **when_p)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1730,7 +1753,7 @@
     *when_p = when;
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, when->cond, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -1762,8 +1785,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_any(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node *parent, struct lysp_node **siblings)
+LY_ERR
+parse_any(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1786,7 +1809,7 @@
     }
 
     /* get name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, any->name, word, word_len);
 
     /* parse substatements */
@@ -1841,7 +1864,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_type_enum_value_pos(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword val_kw, int64_t *value, uint16_t *flags,
+parse_type_enum_value_pos(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword val_kw, int64_t *value, uint16_t *flags,
                           struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -1858,7 +1881,7 @@
     *flags |= LYS_SET_VALUE;
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if (!word_len || (word[0] == '+') || ((word[0] == '0') && (word_len > 1)) || ((val_kw == YANG_VALUE) && !strncmp(word, "-0", 2))) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
@@ -1923,7 +1946,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_type_enum(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword enum_kw, struct lysp_type_enum **enums)
+parse_type_enum(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword enum_kw, struct lysp_type_enum **enums)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -1934,7 +1957,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *enums, enm, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, enum_kw == YANG_ENUM ? Y_STR_ARG : Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, enum_kw == YANG_ENUM ? Y_STR_ARG : Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     if (enum_kw == YANG_ENUM) {
         if (!word_len) {
             LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Enum name must not be zero-length.");
@@ -2001,7 +2024,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_type_fracdigits(struct ly_parser_ctx *ctx, const char **data, uint8_t *fracdig, struct lysp_ext_instance **exts)
+parse_type_fracdigits(struct lys_parser_ctx *ctx, const char **data, uint8_t *fracdig, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word, *ptr;
@@ -2015,7 +2038,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if (!word_len || (word[0] == '0') || !isdigit(word[0])) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
@@ -2064,7 +2087,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_type_reqinstance(struct ly_parser_ctx *ctx, const char **data, uint8_t *reqinst, uint16_t *flags,
+parse_type_reqinstance(struct lys_parser_ctx *ctx, const char **data, uint8_t *reqinst, uint16_t *flags,
                        struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -2079,7 +2102,7 @@
     *flags |= LYS_SET_REQINST;
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *reqinst = 1;
@@ -2114,7 +2137,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_type_pattern_modifier(struct ly_parser_ctx *ctx, const char **data, const char **pat, struct lysp_ext_instance **exts)
+parse_type_pattern_modifier(struct lys_parser_ctx *ctx, const char **data, const char **pat, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2127,7 +2150,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len != 12) || strncmp(word, "invert-match", word_len)) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "modifier");
@@ -2169,7 +2192,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_type_pattern(struct ly_parser_ctx *ctx, const char **data, struct lysp_restr **patterns)
+parse_type_pattern(struct lys_parser_ctx *ctx, const char **data, struct lysp_restr **patterns)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2180,7 +2203,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *patterns, restr, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     /* add special meaning first byte */
     if (buf) {
@@ -2234,7 +2257,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_type(struct ly_parser_ctx *ctx, const char **data, struct lysp_type *type)
+parse_type(struct lys_parser_ctx *ctx, const char **data, struct lysp_type *type)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2248,7 +2271,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, type->name, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -2328,8 +2351,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_leaf(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
+LY_ERR
+parse_leaf(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2353,7 +2376,7 @@
     }
 
     /* get name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, leaf->name, word, word_len);
 
     /* parse substatements */
@@ -2426,8 +2449,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_maxelements(struct ly_parser_ctx *ctx, const char **data, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts)
+LY_ERR
+parse_maxelements(struct lys_parser_ctx *ctx, const char **data, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word, *ptr;
@@ -2442,7 +2465,7 @@
     *flags |= LYS_SET_MAX;
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if (!word_len || (word[0] == '0') || ((word[0] != 'u') && !isdigit(word[0]))) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
@@ -2493,8 +2516,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_minelements(struct ly_parser_ctx *ctx, const char **data, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts)
+LY_ERR
+parse_minelements(struct lys_parser_ctx *ctx, const char **data, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word, *ptr;
@@ -2509,7 +2532,7 @@
     *flags |= LYS_SET_MIN;
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if (!word_len || !isdigit(word[0]) || ((word[0] == '0') && (word_len > 1))) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
@@ -2557,7 +2580,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_orderedby(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+parse_orderedby(struct lys_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2570,7 +2593,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len == 6) && !strncmp(word, "system", word_len)) {
         *flags |= LYS_ORDBY_SYSTEM;
@@ -2605,8 +2628,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_leaflist(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
+LY_ERR
+parse_leaflist(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2630,7 +2653,7 @@
     }
 
     /* get name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, llist->name, word, word_len);
 
     /* parse substatements */
@@ -2715,7 +2738,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_refine(struct ly_parser_ctx *ctx, const char **data, struct lysp_refine **refines)
+parse_refine(struct lys_parser_ctx *ctx, const char **data, struct lysp_refine **refines)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2726,7 +2749,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *refines, rf, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, rf->nodeid, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -2783,7 +2806,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_typedef(struct ly_parser_ctx *ctx, struct lysp_node *parent, const char **data, struct lysp_tpdf **typedefs)
+parse_typedef(struct lys_parser_ctx *ctx, struct lysp_node *parent, const char **data, struct lysp_tpdf **typedefs)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2794,7 +2817,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *typedefs, tpdf, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, tpdf->name, word, word_len);
 
     /* parse substatements */
@@ -2853,7 +2876,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_inout(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword inout_kw, struct lysp_node *parent, struct lysp_action_inout *inout_p)
+parse_inout(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword inout_kw, struct lysp_node *parent, struct lysp_action_inout *inout_p)
 {
     LY_ERR ret = LY_SUCCESS;
     char *word;
@@ -2868,7 +2891,7 @@
     }
 
     /* initiate structure */
-    inout_p->nodetype = LYS_INOUT;
+    inout_p->nodetype = &((struct lysp_action*)parent)->input == inout_p ? LYS_INPUT : LYS_OUTPUT;
     inout_p->parent = parent;
 
     /* parse substatements */
@@ -2934,8 +2957,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_action(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_action **actions)
+LY_ERR
+parse_action(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_action **actions)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2948,7 +2971,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *actions, act, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, act->name, word, word_len);
     act->nodetype = LYS_ACTION;
     act->parent = parent;
@@ -3007,8 +3030,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_notif(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_notif **notifs)
+LY_ERR
+parse_notif(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_notif **notifs)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3021,7 +3044,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *notifs, notif, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, notif->name, word, word_len);
     notif->nodetype = LYS_NOTIF;
     notif->parent = parent;
@@ -3102,8 +3125,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings)
+LY_ERR
+parse_grouping(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3116,7 +3139,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *groupings, grp, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, grp->name, word, word_len);
     grp->nodetype = LYS_GROUPING;
     grp->parent = parent;
@@ -3198,8 +3221,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_augment(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_augment **augments)
+LY_ERR
+parse_augment(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_augment **augments)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3210,7 +3233,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *augments, aug, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, aug->nodeid, word, word_len);
     aug->nodetype = LYS_AUGMENT;
     aug->parent = parent;
@@ -3289,8 +3312,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
+LY_ERR
+parse_uses(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3314,7 +3337,7 @@
     }
 
     /* get name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, uses->name, word, word_len);
 
     /* parse substatements */
@@ -3362,8 +3385,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
+LY_ERR
+parse_case(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3387,7 +3410,7 @@
     }
 
     /* get name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, cas->name, word, word_len);
 
     /* parse substatements */
@@ -3453,8 +3476,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
+LY_ERR
+parse_choice(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3478,7 +3501,7 @@
     }
 
     /* get name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, choice->name, word, word_len);
 
     /* parse substatements */
@@ -3560,8 +3583,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
+LY_ERR
+parse_container(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = 0;
     char *buf, *word;
@@ -3586,7 +3609,7 @@
     }
 
     /* get name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, cont->name, word, word_len);
 
     /* parse substatements */
@@ -3682,8 +3705,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
+LY_ERR
+parse_list(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3708,7 +3731,7 @@
     }
 
     /* get name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, list->name, word, word_len);
 
     /* parse substatements */
@@ -3827,7 +3850,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_yinelement(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
+parse_yinelement(struct lys_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3840,7 +3863,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *flags |= LYS_YINELEM_TRUE;
@@ -3878,7 +3901,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_argument(struct ly_parser_ctx *ctx, const char **data, const char **argument, uint16_t *flags, struct lysp_ext_instance **exts)
+parse_argument(struct lys_parser_ctx *ctx, const char **data, const char **argument, uint16_t *flags, struct lysp_ext_instance **exts)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3891,7 +3914,7 @@
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, *argument, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -3920,7 +3943,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_extension(struct ly_parser_ctx *ctx, const char **data, struct lysp_ext **extensions)
+parse_extension(struct lys_parser_ctx *ctx, const char **data, struct lysp_ext **extensions)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3931,7 +3954,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *extensions, ex, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, ex->name, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -3968,8 +3991,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_deviate(struct ly_parser_ctx *ctx, const char **data, struct lysp_deviate **deviates)
+LY_ERR
+parse_deviate(struct lys_parser_ctx *ctx, const char **data, struct lysp_deviate **deviates)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -3985,7 +4008,7 @@
     uint32_t *d_min, *d_max;
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len == 13) && !strncmp(word, "not-supported", word_len)) {
         dev_mod = LYS_DEV_NOT_SUPPORTED;
@@ -4182,8 +4205,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_deviation(struct ly_parser_ctx *ctx, const char **data, struct lysp_deviation **deviations)
+LY_ERR
+parse_deviation(struct lys_parser_ctx *ctx, const char **data, struct lysp_deviation **deviations)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -4194,7 +4217,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *deviations, dev, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, dev->nodeid, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
@@ -4236,8 +4259,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_feature(struct ly_parser_ctx *ctx, const char **data, struct lysp_feature **features)
+LY_ERR
+parse_feature(struct lys_parser_ctx *ctx, const char **data, struct lysp_feature **features)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -4248,7 +4271,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *features, feat, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, feat->name, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -4285,8 +4308,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_identity(struct ly_parser_ctx *ctx, const char **data, struct lysp_ident **identities)
+LY_ERR
+parse_identity(struct lys_parser_ctx *ctx, const char **data, struct lysp_ident **identities)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -4297,7 +4320,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *identities, ident, LY_EMEM);
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, ident->name, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -4368,8 +4391,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_module(struct ly_parser_ctx *ctx, const char **data, struct lysp_module *mod)
+LY_ERR
+parse_module(struct lys_parser_ctx *ctx, const char **data, struct lysp_module *mod)
 {
     LY_ERR ret = 0;
     char *buf, *word;
@@ -4379,7 +4402,7 @@
     struct lysp_submodule *dup;
 
     /* (sub)module name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, mod->mod->name, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
@@ -4578,8 +4601,8 @@
  *
  * @return LY_ERR values.
  */
-static LY_ERR
-parse_submodule(struct ly_parser_ctx *ctx, const char **data, struct lysp_submodule *submod)
+LY_ERR
+parse_submodule(struct lys_parser_ctx *ctx, const char **data, struct lysp_submodule *submod)
 {
     LY_ERR ret = 0;
     char *buf, *word;
@@ -4589,7 +4612,7 @@
     struct lysp_submodule *dup;
 
     /* submodule name */
-    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, submod->name, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
@@ -4773,10 +4796,10 @@
 }
 
 LY_ERR
-yang_parse_submodule(struct ly_parser_ctx *context, const char *data, struct lysp_submodule **submod)
+yang_parse_submodule(struct lys_parser_ctx *context, const char *data, struct lysp_submodule **submod)
 {
     LY_ERR ret = LY_SUCCESS;
-    char *word, *buf;
+    char *word;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_submodule *mod_p = NULL;
@@ -4805,17 +4828,15 @@
     LY_CHECK_GOTO(ret, cleanup);
 
     /* read some trailing spaces or new lines */
-    ret = get_argument(context, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    if (word) {
-        LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
-               word_len, word);
-        free(buf);
+    while(*data && isspace(*data)) {
+        data++;
+    }
+    if (*data) {
+        LOGVAL_YANG(context, LYVE_SYNTAX, "Trailing garbage \"%.*s%s\" after submodule, expected end-of-input.",
+                    15, data, strlen(data) > 15 ? "..." : "");
         ret = LY_EVALID;
         goto cleanup;
     }
-    assert(!buf);
 
     mod_p->parsing = 0;
     *submod = mod_p;
@@ -4829,10 +4850,10 @@
 }
 
 LY_ERR
-yang_parse_module(struct ly_parser_ctx *context, const char *data, struct lys_module *mod)
+yang_parse_module(struct lys_parser_ctx *context, const char *data, struct lys_module *mod)
 {
     LY_ERR ret = LY_SUCCESS;
-    char *word, *buf;
+    char *word;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_module *mod_p = NULL;
@@ -4862,17 +4883,15 @@
     LY_CHECK_GOTO(ret, cleanup);
 
     /* read some trailing spaces or new lines */
-    ret = get_argument(context, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    if (word) {
-        LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
-               word_len, word);
-        free(buf);
+    while(*data && isspace(*data)) {
+        data++;
+    }
+    if (*data) {
+        LOGVAL_YANG(context, LYVE_SYNTAX, "Trailing garbage \"%.*s%s\" after module, expected end-of-input.",
+                    15, data, strlen(data) > 15 ? "..." : "");
         ret = LY_EVALID;
         goto cleanup;
     }
-    assert(!buf);
 
     mod_p->parsing = 0;
     mod->parsed = mod_p;
diff --git a/src/parser_yin.c b/src/parser_yin.c
index af8b400..7d406f5 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -11,31 +11,20 @@
  *
  *     https://opensource.org/licenses/BSD-3-Clause
  */
+#include "common.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 
-#include "common.h"
 #include "context.h"
-#include "libyang.h"
+#include "dict.h"
 #include "xml.h"
+#include "tree.h"
+#include "tree_schema.h"
 #include "tree_schema_internal.h"
-
-enum YIN_ARGUMENT {
-    YIN_ARG_UNKNOWN = 0,   /**< parsed argument can not be matched with any known yin argument keyword */
-    YIN_ARG_NAME,          /**< argument name */
-    YIN_ARG_TARGET_NODE,   /**< argument target-node */
-    YIN_ARG_MODULE,        /**< argument module */
-    YIN_ARG_VALUE,         /**< argument value */
-    YIN_ARG_TEXT,          /**< argument text */
-    YIN_ARG_CONDITION,     /**< argument condition */
-    YIN_ARG_URI,           /**< argument uri */
-    YIN_ARG_DATE,          /**< argument data */
-    YIN_ARG_TAG,           /**< argument tag */
-    YIN_ARG_XMLNS,         /**< argument xmlns */
-    YIN_ARG_NONE,          /**< special value to specify no valid argument except xmlns, do not confuse with YIN_ARG_NONE */
-};
+#include "parser_yin.h"
 
 /**
  * @brief Match argument name.
@@ -114,39 +103,15 @@
 }
 
 /**
- * @brief parse xmlns statement
- *
- * @param[in] xml_ctx XML parser context.
- * @param[in, out] data Data to reda from.
- * @param[in] prefix
- */
-LY_ERR
-parse_xmlns(struct lyxml_context *xml_ctx, const char **data, const char *prefix, size_t prefix_len, char *element)
-{
-    char *buf = NULL, *out = NULL;
-    size_t buf_len = 0, out_len = 0;
-    int dynamic = 0;
-    LY_ERR ret = LY_SUCCESS;
-
-    ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
-    LY_CHECK_RET(ret != LY_SUCCESS, ret);
-    LY_CHECK_ERR_RET(out_len == 0, LOGVAL_YANG(xml_ctx, LYVE_SYNTAX_YIN, "Missing value of xmlns attribute"), LY_EEXIST);
-    lyxml_ns_add(xml_ctx, element, prefix, prefix_len, out, out_len);
-
-    return LY_SUCCESS;
-}
-
-/**
- * @brief parse yin argument and xmlns definition
+ * @brief parse yin argument
  *
  * @param[in] xml_ctx XML parser context.
  * @param[in, out] data Data to read from.
  * @param[in] arg_type Type of argument that is expected in parsed element (use YIN_ARG_NONE for elements without special arguments).
  * @param[out] arg_val Where value of argument should be stored. Can be NULL if arg_type is spedified as YIN_ARG_NONE.
- * @param[in] elem_name Name of currently parse element, used to create namespace scoping
  */
 static LY_ERR
-yin_parse_attribute(struct lyxml_context *xml_ctx, const char **data, enum YIN_ARGUMENT arg_type, const char **arg_val, char *elem_name)
+yin_parse_attribute(struct lyxml_context *xml_ctx, const char **data, enum YIN_ARGUMENT arg_type, const char **arg_val)
 {
     LY_ERR ret = LY_SUCCESS;
     const char *prefix, *name;
@@ -163,22 +128,17 @@
 
         arg = match_argument_name(name, name_len);
 
-        if (arg == YIN_ARG_XMLNS) {
-            parse_xmlns(xml_ctx, data, prefix, prefix_len, elem_name);
-        } else if (arg == arg_type) {
+        if (arg == arg_type) {
             LY_CHECK_RET(ret);
             ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
             LY_CHECK_RET(ret);
             *arg_val = lydict_insert(xml_ctx->ctx, out, out_len);
             LY_CHECK_ERR_RET(!(*arg_val), LOGMEM(xml_ctx->ctx), LY_EMEM);
+        } else if (arg_type == YIN_ARG_NONE) {
+            continue;
         } else {
-            /* unrecognized attribute, still can be namespace definition eg. xmlns:foo=.... */
-            /* TODO this might be extension and not ns definition */
-            if (match_argument_name(prefix, prefix_len) == YIN_ARG_XMLNS) {
-                /* in this case prefix of namespace is actually name of attribute */
-                parse_xmlns(xml_ctx, data, name, name_len, elem_name);
-            } else {
-                /* unrecognized or unexpected attribute */
+            /* unrecognized or unexpected attribute */
+            if (name) {
                 LOGERR(xml_ctx->ctx, LY_EDENIED, "Invalid argument in namespace element");
                 return LY_EVALID;
             }
@@ -192,20 +152,18 @@
  * @brief Parse content of whole element as text.
  *
  * @param[in] xml_ctx Xml context.
- * @param[in] element_name Name of element, name is necesary to remove xmlns definitions at the end of element
  * @param[in] data Data to read from.
  * @param[out] value Where content of element should be stored.
  */
 LY_ERR
-parse_text_element(struct lyxml_context *xml_ctx, char *element_name, const char **data, const char **value)
+parse_text_element(struct lyxml_context *xml_ctx, const char **data, const char **value)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf = NULL, *out = NULL;
-    size_t buf_len = 0, out_len = 0, prefix_len, name_len;
+    size_t buf_len = 0, out_len = 0;
     int dynamic;
-    const char *prefix, *name;
 
-    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_NONE, NULL, element_name);
+    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_NONE, NULL);
     LY_CHECK_RET(ret != LY_SUCCESS, ret);
     LY_CHECK_RET(xml_ctx->status != LYXML_ELEM_CONTENT, LY_EVALID);
 
@@ -216,65 +174,9 @@
         LY_CHECK_ERR_RET(!(*value), LOGMEM(xml_ctx->ctx), LY_EMEM);
     }
 
-    lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
-    lyxml_ns_rm(xml_ctx, element_name);
     return LY_SUCCESS;
 }
 
-// LY_ERR
-// parser_belongs_to(struct lyxml_context *xml_ctx, const char **data, const char **belongsto, const char **prefix, struct lysp_ext **extensions)
-// {
-//     enum yang_keyword kw = YANG_NONE;
-//     LY_ERR ret = LY_SUCCESS;
-//     const char *prefix_out, *name;
-//     size_t prefix_len, name_len;
-
-//     char *buf = NULL, *out = NULL;
-//     size_t buf_len = 0, out_len = 0;
-//     int dynamic;
-
-//     /* check if belongs-to has argument module */
-//     ret = lyxml_get_attribute(xml_ctx, data, &prefix_out, &prefix_len, &name, &name_len);
-//     LY_CHECK_RET1(ret);
-//     if (match_argument_name(name, name_len) != YIN_ARG_MODULE) {
-//         LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Invalid argument name \"%s\", expected \"module\".", name);
-//         return LY_EINVAL;
-//     }
-
-//     /* read content of argument */
-//     ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
-//     LY_CHECK_RET1(ret);
-//     *belongsto = lydict_insert(xml_ctx->ctx, out, out_len);
-//     LY_CHECK_ERR_RET(!belongsto, LOGMEM(xml_ctx->ctx), LY_EMEM);
-
-//     /* read substatements */
-//     while (xml_ctx->status == LYXML_ATTRIBUTE) {
-//         ret = lyxml_get_attribute(xml_ctx, data, &prefix_out, &prefix_len, &name, &name_len);
-//         LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), ret);
-//         kw = match_keyword(name);
-
-//         switch (kw) {
-//         case YANG_PREFIX:
-//             ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
-//             *prefix = lydict_insert(xml_ctx->ctx, out, out_len);
-//             break;
-//         case YANG_CUSTOM:
-//             /* TODO parse extension */
-//             break;
-//         default:
-//             LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Unexpected attribute");
-//             return LY_EVALID;
-//         }
-//     }
-
-//     if (!prefix) {
-//         LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Missing prefix");
-//         return LY_EVALID;
-//     }
-
-//     return LY_SUCCESS;
-// }
-
 /**
  * @brief Parse namespace statement.
  *
@@ -289,11 +191,9 @@
 {
     LY_ERR ret = LY_SUCCESS;
 
-    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_URI, namespace, "namespace");
+    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_URI, namespace);
     LY_CHECK_RET(ret != LY_SUCCESS, ret);
 
-    /* remove local xmlns definitions */
-    lyxml_ns_rm(xml_ctx, "namespace");
     return LY_SUCCESS;
 }
 
@@ -312,10 +212,9 @@
     LY_ERR ret = LY_SUCCESS;
 
     /* parse attributes */
-    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_VALUE, prefix, "prefix");
+    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_VALUE, prefix);
     LY_CHECK_RET(ret != LY_SUCCESS, ret);
-    /* remove local xmlns definitions */
-    ret = lyxml_ns_rm(xml_ctx, "prefix");
+
     return ret;
 }
 
@@ -329,11 +228,10 @@
         return LY_EVALID;
     }
 
-    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_DATE, (const char **)&rev, "revision-date");
+    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_DATE, (const char **)&rev);
     LY_CHECK_RET(ret != LY_SUCCESS, ret);
-    LY_CHECK_RET(lysp_check_date((struct ly_parser_ctx *)xml_ctx, rev, strlen(rev), "revision-date") != LY_SUCCESS, LY_EVALID);
+    LY_CHECK_RET(lysp_check_date((struct lys_parser_ctx *)xml_ctx, rev, strlen(rev), "revision-date") != LY_SUCCESS, LY_EVALID);
 
-    ret = lyxml_ns_rm(xml_ctx, "import");
     return ret;
 }
 
@@ -350,7 +248,7 @@
     LY_ARRAY_NEW_RET(xml_ctx->ctx, *imports, imp, LY_EVALID);
 
     /* parse import attributes  */
-    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_MODULE, &imp->name, "import");
+    ret = yin_parse_attribute(xml_ctx, data, YIN_ARG_MODULE, &imp->name);
     while ((ret = lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len) == LY_SUCCESS && name != NULL)) {
         kw = match_keyword(name, name_len, prefix_len);
         switch (kw) {
@@ -360,14 +258,14 @@
             break;
         case YANG_DESCRIPTION:
             LY_CHECK_ERR_RET(imp->dsc, LOGVAL_YANG(xml_ctx, LY_VCODE_DUPSTMT, "description"), LY_EVALID);
-            parse_text_element(xml_ctx, "description", data, &imp->dsc);
+            parse_text_element(xml_ctx, data, &imp->dsc);
             break;
         case YANG_REFERENCE:
             LY_CHECK_ERR_RET(imp->ref, LOGVAL_YANG(xml_ctx, LY_VCODE_DUPSTMT, "reference"), LY_EVALID);
-            parse_text_element(xml_ctx, "reference", data, &imp->ref);
+            parse_text_element(xml_ctx, data, &imp->ref);
             break;
         case YANG_REVISION_DATE:
-            yin_parse_revision_date(xml_ctx, data, &imp->rev, &imp->exts);
+            yin_parse_revision_date(xml_ctx, data, imp->rev, &imp->exts);
             break;
         case YANG_CUSTOM:
             /* TODO parse extension */
@@ -380,7 +278,6 @@
 
     /* TODO add log macro and log error */
     LY_CHECK_RET(!imp->prefix);
-    ret = lyxml_ns_rm(xml_ctx, "import");
     return ret;
 }
 
@@ -415,9 +312,6 @@
         arg = match_argument_name(name, name_len);
 
         switch (arg) {
-        case YIN_ARG_XMLNS:
-            parse_xmlns(xml_ctx, data, prefix, prefix_len, "module");
-            break;
         case YIN_ARG_NAME:
             /* check for multiple definitions of name */
             LY_CHECK_ERR_RET((*mod)->mod->name, LOGVAL_YANG(xml_ctx, LYVE_SYNTAX_YIN, "Duplicit definition of module name \"%s\"", (*mod)->mod->name), LY_EEXIST);
@@ -431,16 +325,16 @@
             (*mod)->mod->name = lydict_insert(xml_ctx->ctx, out, out_len);
             LY_CHECK_ERR_RET(!(*mod)->mod->name, LOGMEM(xml_ctx->ctx), LY_EMEM);
             break;
+
         default:
-            /* unrecognized attribute, still can be namespace definition eg. xmlns:foo=.... */
-            if (match_argument_name(prefix, prefix_len) == YIN_ARG_XMLNS) {
-                /* in this case prefix of namespace is actually name of attribute */
-                parse_xmlns(xml_ctx, data, name, name_len, "module");
-            } else {
-                /* unrecognized or unexpected attribute */
+            /* unrecognized attribute */
+            /* unrecognized or unexpected attribute */
+            /* TODO probably still can be from extension */
+            if (arg != YIN_ARG_UNKNOWN || name) {
                 LOGERR(xml_ctx->ctx, LY_EDENIED, "Invalid argument in module element");
                 return LY_EVALID;
             }
+
             break;
         }
     }
@@ -531,16 +425,16 @@
 
             /* meta */
             case YANG_ORGANIZATION:
-                LY_CHECK_RET(parse_text_element(xml_ctx, "organization", data, &(*mod)->mod->org));
+                LY_CHECK_RET(parse_text_element(xml_ctx, data, &(*mod)->mod->org));
                 break;
             case YANG_CONTACT:
-                LY_CHECK_RET(parse_text_element(xml_ctx, "contact", data, &(*mod)->mod->contact));
+                LY_CHECK_RET(parse_text_element(xml_ctx, data, &(*mod)->mod->contact));
                 break;
             case YANG_DESCRIPTION:
-                LY_CHECK_RET(parse_text_element(xml_ctx, "description", data, &(*mod)->mod->dsc));
+                LY_CHECK_RET(parse_text_element(xml_ctx, data, &(*mod)->mod->dsc));
                 break;
             case YANG_REFERENCE:
-                LY_CHECK_RET(parse_text_element(xml_ctx, "reference", data, &(*mod)->mod->ref));
+                LY_CHECK_RET(parse_text_element(xml_ctx, data, &(*mod)->mod->ref));
                 break;
 
             default:
@@ -550,7 +444,6 @@
         }
     }
 
-    lyxml_ns_rm(xml_ctx, "module");
     return ret;
 }
 
@@ -628,7 +521,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     enum yang_keyword kw = YANG_NONE;
-    struct ly_parser_ctx parser_ctx;
+    struct lys_parser_ctx parser_ctx;
     struct lyxml_context *xml_ctx = (struct lyxml_context *)&parser_ctx;
     struct lysp_module *mod_p = NULL;
     const char *prefix, *name;
diff --git a/src/plugins_types.c b/src/plugins_types.c
new file mode 100644
index 0000000..244b7ff
--- /dev/null
+++ b/src/plugins_types.c
@@ -0,0 +1,1039 @@
+/**
+ * @file plugin_types.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in types plugins and interface for user types plugins.
+ *
+ * Copyright (c) 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
+ */
+#include "common.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "plugins_types.h"
+#include "dict.h"
+#include "tree_schema.h"
+
+API LY_ERR
+parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err)
+{
+    char *errmsg = NULL;
+    char *strptr = NULL;
+    int64_t i;
+
+    LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
+
+    /* consume leading whitespaces */
+    for (;value_len && isspace(*value); ++value, --value_len);
+
+    if (!value || !value[0] || !value_len) {
+        asprintf(&errmsg, "Invalid empty %s value.", datatype);
+        goto error;
+    }
+
+    /* convert to 64-bit integer, all the redundant characters are handled */
+    errno = 0;
+
+    /* parse the value */
+    i = strtoll(value, &strptr, base);
+    if (errno || strptr == value || (i < min) || (i > max)) {
+        goto error;
+    } else if (strptr && *strptr && strptr < value + value_len) {
+        while (isspace(*strptr)) {
+            ++strptr;
+        }
+        if (*strptr && strptr < value + value_len) {
+            goto error;
+        }
+    }
+
+    if (ret) {
+        *ret = i;
+    }
+    return LY_SUCCESS;
+
+error:
+    if (!errmsg) {
+        asprintf(&errmsg, "Invalid %s value \"%.*s\".", datatype, (int)value_len, value);
+    }
+    *err = ly_err_new(LY_LLERR, LY_EINVAL, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    return LY_EVALID;
+}
+
+API LY_ERR
+parse_uint(const char *datatype, int base, uint64_t min, uint64_t max, const char *value, size_t value_len, uint64_t *ret, struct ly_err_item **err)
+{
+    char *errmsg = NULL;
+    char *strptr = NULL;
+    uint64_t u;
+
+    LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
+
+    /* consume leading whitespaces */
+    for (;value_len && isspace(*value); ++value, --value_len);
+
+    if (!value || !value[0] || !value_len) {
+        asprintf(&errmsg, "Invalid empty %s value.", datatype);
+        goto error;
+    }
+
+    *err = NULL;
+    errno = 0;
+    u = strtoull(value, &strptr, base);
+    if (errno || strptr == value || (u < min) || (u > max)) {
+        goto error;
+    } else if (strptr && *strptr && strptr < value + value_len) {
+        while (isspace(*strptr)) {
+            ++strptr;
+        }
+        if (*strptr && strptr < value + value_len) {
+            goto error;
+        }
+    } else if (u != 0 && value[0] == '-') {
+        goto error;
+    }
+
+    if (ret) {
+        *ret = u;
+    }
+    return LY_SUCCESS;
+
+error:
+    if (!errmsg) {
+        if (strptr && strptr != value + value_len) {
+            asprintf(&errmsg, "Invalid %lu. character of %s value \"%.*s\".",
+                     1 + (unsigned long int)(strptr - value), datatype, (int)value_len, value);
+        } else {
+            asprintf(&errmsg, "Invalid %s value \"%.*s\".", datatype, (int)value_len, value);
+        }
+    }
+    *err = ly_err_new(LY_LLERR, LY_EINVAL, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    return LY_EVALID;
+}
+
+API LY_ERR
+parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err)
+{
+    LY_ERR rc = LY_EINVAL;
+    char *errmsg = NULL;
+    char *valcopy = NULL;
+    size_t fraction = 0, size, len = 0, trailing_zeros;
+    int64_t d;
+
+    /* consume leading whitespaces */
+    for (;value_len && isspace(*value); ++value, --value_len);
+
+    /* parse value */
+    if (!value_len) {
+        errmsg = strdup("Invalid empty decimal64 value.");
+        goto error;
+    } else if (!isdigit(value[len]) && (value[len] != '-') && (value[len] != '+')) {
+        asprintf(&errmsg, "Invalid %lu. character of decimal64 value \"%.*s\".",
+                 len + 1, (int)value_len, value);
+        goto error;
+    }
+
+    if ((value[len] == '-') || (value[len] == '+')) {
+        ++len;
+    }
+
+    while (len < value_len && isdigit(value[len])) {
+        ++len;
+    }
+
+    trailing_zeros = 0;
+    if (len < value_len && ((value[len] != '.') || !isdigit(value[len + 1]))) {
+        goto decimal;
+    }
+    fraction = len;
+    ++len;
+    while (len < value_len && isdigit(value[len])) {
+        if (value[len] == '0') {
+            ++trailing_zeros;
+        } else {
+            trailing_zeros = 0;
+        }
+        ++len;
+    }
+    len = len - trailing_zeros;
+
+decimal:
+    if (len - 1 - fraction > fraction_digits) {
+        asprintf(&errmsg, "Value \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.", (int)len, value,
+                 fraction_digits);
+        goto error;
+    }
+    if (fraction) {
+        size = len + (fraction_digits - (len - 1 - fraction));
+    } else {
+        size = len + fraction_digits + 1;
+    }
+
+    if (len + trailing_zeros < value_len) {
+        /* consume trailing whitespaces to check that there is nothing after it */
+        unsigned long int u;
+        for (u = len + trailing_zeros; u < value_len && isspace(value[u]); ++u);
+        if (u != value_len) {
+            asprintf(&errmsg, "Invalid %lu. character of decimal64 value \"%.*s\".",
+                     u + 1, (int)value_len, value);
+            goto error;
+        }
+    }
+
+    /* prepare value string without decimal point to easily parse using standard functions */
+    valcopy = malloc(size * sizeof *valcopy);
+    if (!valcopy) {
+        *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
+        return LY_EMEM;
+    }
+
+    valcopy[size - 1] = '\0';
+    if (fraction) {
+        memcpy(&valcopy[0], &value[0], fraction);
+        memcpy(&valcopy[fraction], &value[fraction + 1], len - 1 - (fraction));
+        /* add trailing zero characters */
+        memset(&valcopy[len - 1], '0', fraction_digits - (len - 1 - fraction));
+    } else {
+        memcpy(&valcopy[0], &value[0], len);
+        /* add trailing zero characters */
+        memset(&valcopy[len], '0', fraction_digits);
+    }
+
+    rc = parse_int("decimal64", 10, INT64_C(-9223372036854775807) - INT64_C(1), INT64_C(9223372036854775807), valcopy, len, &d, err);
+    if (!rc && ret) {
+        *ret = d;
+    }
+    free(valcopy);
+
+error:
+    if (errmsg) {
+        *err = ly_err_new(LY_LLERR, LY_EINVAL, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    }
+    return rc;
+}
+
+API LY_ERR
+ly_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    int rc;
+    unsigned int u;
+    char *errmsg;
+    pcre2_match_data *match_data = NULL;
+
+    LY_CHECK_ARG_RET(NULL, str, err, LY_EINVAL);
+
+    LY_ARRAY_FOR(patterns, u) {
+        match_data = pcre2_match_data_create_from_pattern(patterns[u]->code, NULL);
+        if (!match_data) {
+            *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
+            return LY_EMEM;
+        }
+
+        rc = pcre2_match(patterns[u]->code, (PCRE2_SPTR)str, str_len, 0, PCRE2_ANCHORED | PCRE2_ENDANCHORED, match_data, NULL);
+        if (rc == PCRE2_ERROR_NOMATCH) {
+            asprintf(&errmsg, "String \"%.*s\" does not conforms to the %u. pattern restriction of its type.",
+                     (int)str_len, str, u + 1);
+            *err = ly_err_new(LY_LLERR, LY_ESYS, 0, errmsg, NULL, NULL);
+            ret = LY_EVALID;
+            goto cleanup;
+        } else if (rc < 0) {
+            /* error */
+            PCRE2_UCHAR pcre2_errmsg[256] = {0};
+            pcre2_get_error_message(rc, pcre2_errmsg, 256);
+            *err = ly_err_new(LY_LLERR, LY_ESYS, 0, strdup((const char*)pcre2_errmsg), NULL, NULL);
+            ret = LY_ESYS;
+            goto cleanup;
+        }
+
+    cleanup:
+        pcre2_match_data_free(match_data);
+        if (ret) {
+            break;
+        }
+    }
+
+    return ret;
+}
+
+API LY_ERR
+ly_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *canonized, struct ly_err_item **err)
+{
+    unsigned int u;
+    char *errmsg = NULL;
+
+    LY_ARRAY_FOR(range->parts, u) {
+        if (basetype < LY_TYPE_DEC64) {
+            /* unsigned */
+            if ((uint64_t)value < range->parts[u].min_u64) {
+                if (range->emsg) {
+                    errmsg = strdup(range->emsg);
+                } else {
+                    asprintf(&errmsg, "%s \"%s\" does not satisfy the %s constraint.",
+                           (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? "Length" : "Value", canonized,
+                           (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? "length" : "range");
+                }
+                goto error;
+            } else if ((uint64_t)value <= range->parts[u].max_u64) {
+                /* inside the range */
+                return LY_SUCCESS;
+            } else if (u == LY_ARRAY_SIZE(range->parts) - 1) {
+                /* we have the last range part, so the value is out of bounds */
+                if (range->emsg) {
+                    errmsg = strdup(range->emsg);
+                } else {
+                    asprintf(&errmsg, "%s \"%s\" does not satisfy the %s constraint.",
+                           (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? "Length" : "Value", canonized,
+                           (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? "length" : "range");
+                }
+                goto error;
+            }
+        } else {
+            /* signed */
+            if (value < range->parts[u].min_64) {
+                if (range->emsg) {
+                    errmsg = strdup(range->emsg);
+                } else {
+                    asprintf(&errmsg, "Value \"%s\" does not satisfy the range constraint.", canonized);
+                }
+                goto error;
+            } else if (value < range->parts[u].max_64) {
+                /* inside the range */
+                return LY_SUCCESS;
+            } else if (u == LY_ARRAY_SIZE(range->parts) - 1) {
+                /* we have the last range part, so the value is out of bounds */
+                if (range->emsg) {
+                    errmsg = strdup(range->emsg);
+                } else {
+                    asprintf(&errmsg, "Value \"%s\" does not satisfy the range constraint.", canonized);
+                }
+                goto error;
+            }
+        }
+    }
+
+    return LY_SUCCESS;
+
+error:
+    *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, range->eapptag ? strdup(range->eapptag) : NULL);
+    return LY_EVALID;
+}
+
+static LY_ERR
+ly_type_parse_int_builtin(LY_DATA_TYPE basetype, const char *value, size_t value_len, int options, int64_t *val, struct ly_err_item **err)
+{
+    switch (basetype) {
+    case LY_TYPE_INT8:
+        return parse_int("int16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, INT64_C(-128), INT64_C(127), value, value_len, val, err);
+    case LY_TYPE_INT16:
+        return parse_int("int16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, INT64_C(-32768), INT64_C(32767), value, value_len, val, err);
+    case LY_TYPE_INT32:
+        return parse_int("int32", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10,
+                         INT64_C(-2147483648), INT64_C(2147483647), value, value_len, val, err);
+    case LY_TYPE_INT64:
+        return parse_int("int64", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10,
+                         INT64_C(-9223372036854775807) - INT64_C(1), INT64_C(9223372036854775807), value, value_len, val, err);
+    default:
+        LOGINT(NULL);
+        return LY_EINVAL;
+    }
+}
+/**
+ * @brief Validate and canonize value of the YANG built-in signed integer types.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_int(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                     const char **canonized, struct ly_err_item **err, void **priv)
+{
+    LY_ERR ret;
+    int64_t i;
+    char *str;
+    struct lysc_type_num *type_num = (struct lysc_type_num *)type;
+
+    LY_CHECK_RET(ly_type_parse_int_builtin(type->basetype, value, value_len, options, &i, err));
+    asprintf(&str, "%"PRId64, i);
+
+    /* range of the number */
+    if (type_num->range) {
+        LY_CHECK_ERR_RET(ret = ly_type_validate_range(type->basetype, type_num->range, i, str, err), free(str), ret);
+    }
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        *canonized = lydict_insert_zc(ctx, str);
+    } else {
+        free(str);
+    }
+
+    if (options & LY_TYPE_OPTS_STORE) {
+        /* save for the store callback */
+        *priv = malloc(sizeof i);
+        if (!(*priv)) {
+            *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
+            return LY_EMEM;
+        }
+        *(int64_t*)(*priv) = i;
+    }
+
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Store value of the YANG built-in signed integer types.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_int(struct ly_ctx *UNUSED(ctx), struct lysc_type *type, int options,
+                  struct lyd_value *value, struct ly_err_item **err, void **priv)
+{
+    int64_t i;
+
+    if (options & LY_TYPE_OPTS_VALIDATE) {
+        /* the value was prepared by ly_type_validate_int() */
+        i = *(int64_t*)(*priv);
+        free(*priv);
+    } else {
+        LY_CHECK_RET(ly_type_parse_int_builtin(type->basetype, value->canonized, strlen(value->canonized), options, &i, err));
+    }
+
+    /* store the result */
+    value->int64 = i;
+
+    return LY_SUCCESS;
+}
+
+static LY_ERR
+ly_type_parse_uint_builtin(LY_DATA_TYPE basetype, const char *value, size_t value_len, int options, uint64_t *val, struct ly_err_item **err)
+{
+    switch (basetype) {
+    case LY_TYPE_UINT8:
+        return parse_uint("uint16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, 0, UINT64_C(255), value, value_len, val, err);
+    case LY_TYPE_UINT16:
+        return parse_uint("uint16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, 0, UINT64_C(65535), value, value_len, val, err);
+    case LY_TYPE_UINT32:
+        return parse_uint("uint32", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, 0, UINT64_C(4294967295), value, value_len, val, err);
+    case LY_TYPE_UINT64:
+        return parse_uint("uint64", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, 0, UINT64_C(18446744073709551615), value, value_len, val, err);
+    default:
+        LOGINT(NULL);
+        return LY_EINVAL;
+    }
+}
+
+/**
+ * @brief Validate and canonize value of the YANG built-in unsigned integer types.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_uint(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                      const char **canonized, struct ly_err_item **err, void **priv)
+{
+    LY_ERR ret;
+    uint64_t u;
+    struct lysc_type_num* type_num = (struct lysc_type_num*)type;
+    char *str;
+
+    LY_CHECK_RET(ly_type_parse_uint_builtin(type->basetype, value, value_len, options, &u, err));
+    asprintf(&str, "%"PRIu64, u);
+
+    /* range of the number */
+    if (type_num->range) {
+        LY_CHECK_ERR_RET(ret = ly_type_validate_range(type->basetype, type_num->range, u, str, err), free(str), ret);
+    }
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        *canonized = lydict_insert_zc(ctx, str);
+    } else {
+        free(str);
+    }
+
+    if (options & LY_TYPE_OPTS_STORE) {
+        /* save for the store callback */
+        *priv = malloc(sizeof u);
+        if (!(*priv)) {
+            *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
+            return LY_EMEM;
+        }
+        *(uint64_t*)(*priv) = u;
+    }
+
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Store value of the YANG built-in unsigned integer types.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_uint(struct ly_ctx *UNUSED(ctx), struct lysc_type *type, int options,
+                   struct lyd_value *value, struct ly_err_item **err, void **priv)
+{
+    uint64_t u;
+
+    if (options & LY_TYPE_OPTS_VALIDATE) {
+        /* the value was prepared by ly_type_validate_uint() */
+        u = *(uint64_t*)(*priv);
+        free(*priv);
+    } else {
+        LY_CHECK_RET(ly_type_parse_uint_builtin(type->basetype, value->canonized, strlen(value->canonized), options, &u, err));
+    }
+
+    /* store the result */
+    value->uint64 = u;
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate and canonize value of the YANG built-in decimal64 types.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_decimal64(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                           const char **canonized, struct ly_err_item **err, void **priv)
+{
+    int64_t d;
+    struct lysc_type_dec* type_dec = (struct lysc_type_dec*)type;
+    char buf[22];
+
+    if (!value || !value[0] || !value_len) {
+        *err = ly_err_new(LY_LLERR, LY_EINVAL, LYVE_RESTRICTION, strdup("Invalid empty decimal64 value."), NULL, NULL);
+        return LY_EVALID;
+    }
+
+    LY_CHECK_RET(parse_dec64(type_dec->fraction_digits, value, value_len, &d, err));
+    /* prepare canonized value */
+    if (d) {
+        int count = sprintf(buf, "%"PRId64" ", d);
+        if ( (d > 0 && (count - 1) <= type_dec->fraction_digits)
+             || (count - 2) <= type_dec->fraction_digits ) {
+            /* we have 0. value, print the value with the leading zeros
+             * (one for 0. and also keep the correct with of num according
+             * to fraction-digits value)
+             * for (num<0) - extra character for '-' sign */
+            count = sprintf(buf, "%0*"PRId64" ", (d > 0) ? (type_dec->fraction_digits + 1) : (type_dec->fraction_digits + 2), d);
+        }
+        for (int i = type_dec->fraction_digits, j = 1; i > 0 ; i--) {
+            if (j && i > 1 && buf[count - 2] == '0') {
+                /* we have trailing zero to skip */
+                buf[count - 1] = '\0';
+            } else {
+                j = 0;
+                buf[count - 1] = buf[count - 2];
+            }
+            count--;
+        }
+        buf[count - 1] = '.';
+    } else {
+        /* zero */
+        sprintf(buf, "0.0");
+    }
+
+    /* range of the number */
+    if (type_dec->range) {
+        LY_CHECK_RET(ly_type_validate_range(type->basetype, type_dec->range, d, buf, err));
+    }
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        *canonized = lydict_insert(ctx, buf, strlen(buf));
+    }
+    if (options & LY_TYPE_OPTS_STORE) {
+        /* save for the store callback */
+        *priv = malloc(sizeof d);
+        if (!(*priv)) {
+            *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
+            return LY_EMEM;
+        }
+        *(int64_t*)(*priv) = d;
+    }
+
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Store value of the YANG built-in decimal64 types.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_decimal64(struct ly_ctx *UNUSED(ctx), struct lysc_type *UNUSED(type), int options,
+                        struct lyd_value *value, struct ly_err_item **UNUSED(err), void **priv)
+{
+    int64_t d;
+
+    if (options & LY_TYPE_OPTS_VALIDATE) {
+        /* the value was prepared by ly_type_validate_uint() */
+        d = *(int64_t*)(*priv);
+        free(*priv);
+    } else {
+        /* TODO if there is usecase for store without validate */
+        LOGINT(NULL);
+        return LY_EINT;
+    }
+
+    /* store the result */
+    value->dec64 = d;
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate and canonize value of the YANG built-in binary type.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_binary(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                        const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
+{
+    size_t start = 0, stop = 0, count = 0, u, termination = 0;
+    struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
+    char *errmsg;
+
+    /* initiate */
+    *err = NULL;
+
+    /* validate characters and remember the number of octets for length validation */
+    if (value && value_len) {
+        /* silently skip leading/trailing whitespaces */
+        for (start = 0; (start < value_len) && isspace(value[start]); start++);
+        for (stop = value_len - 1; stop > start && isspace(value[stop]); stop--);
+        if (start == stop) {
+            /* empty string */
+            goto finish;
+        }
+
+        for (count = 0, u = start; u <= stop; u++) {
+            if (value[u] == '\n') {
+                /* newline formatting */
+                continue;
+            }
+            count++;
+
+            if ((value[u] < '/' && value[u] != '+') ||
+                    (value[u] > '9' && value[u] < 'A') ||
+                    (value[u] > 'Z' && value[u] < 'a') || value[u] > 'z') {
+                /* non-encoding characters */
+                if (value[u] == '=') {
+                    /* padding */
+                    if (u == stop - 1 && value[stop] == '=') {
+                        termination = 2;
+                        count++;
+                        u++;
+                    } else if (u == stop){
+                        termination = 1;
+                    }
+                }
+                if (!termination) {
+                    /* error */
+                    asprintf(&errmsg, "Invalid Base64 character (%c).", value[u]);
+                    goto error;
+                }
+            }
+        }
+    }
+
+finish:
+    if (count & 3) {
+        /* base64 length must be multiple of 4 chars */
+        errmsg = strdup("Base64 encoded value length must be divisible by 4.");
+        goto error;
+    }
+
+    /* length of the encoded string */
+    if (type_bin->length) {
+        char buf[22];
+        uint64_t len = ((count / 4) * 3) - termination;
+        snprintf(buf, 22, "%"PRIu64, len);
+        LY_CHECK_RET(ly_type_validate_range(LY_TYPE_BINARY, type_bin->length, len, buf, err));
+    }
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        if (start != 0 || stop != value_len) {
+            *canonized = lydict_insert_zc(ctx, strndup(&value[start], stop + 1 - start));
+        } else if (options & LY_TYPE_OPTS_DYNAMIC) {
+            *canonized = lydict_insert_zc(ctx, (char*)value);
+            value = NULL;
+        } else {
+            *canonized = lydict_insert(ctx, value_len ? value : "", value_len);
+        }
+    }
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+
+    return LY_SUCCESS;
+
+error:
+    if (!*err) {
+        *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    }
+    return (*err)->no;
+}
+
+/**
+ * @brief Validate value of the YANG built-in string type.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_string(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                        const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
+{
+    struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+
+    /* initiate */
+    *err = NULL;
+
+    /* length restriction of the string */
+    if (type_str->length) {
+        char buf[22];
+        snprintf(buf, 22, "%lu", value_len);
+        LY_CHECK_RET(ly_type_validate_range(LY_TYPE_BINARY, type_str->length, value_len, buf, err));
+    }
+
+    /* pattern restrictions */
+    LY_CHECK_RET(ly_type_validate_patterns(type_str->patterns, value, value_len, err));
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        if (options & LY_TYPE_OPTS_DYNAMIC) {
+            *canonized = lydict_insert_zc(ctx, (char*)value);
+            value = NULL;
+        } else {
+            *canonized = lydict_insert(ctx, value_len ? value : "", value_len);
+        }
+    }
+
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate and canonize value of the YANG built-in bits type.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_bits(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                       const char **canonized, struct ly_err_item **err, void **priv)
+{
+    LY_ERR ret = LY_EVALID;
+    size_t item_len;
+    const char *item;
+    struct ly_set *items = NULL, *items_ordered = NULL;
+    size_t buf_size = 0;
+    char *buf = NULL;
+    size_t index;
+    unsigned int u, v;
+    char *errmsg = NULL;
+    struct lysc_type_bits *type_bits = (struct lysc_type_bits*)type;
+    int iscanonical = 1;
+    size_t ws_count;
+    size_t lws_count; /* leading whitespace count */
+
+    /* remember the present items for further work */
+    items = ly_set_new();
+    LY_CHECK_RET(!items, LY_EMEM);
+
+    for (index = ws_count = lws_count = 0; index < value_len; index++, ws_count++) {
+        if (isspace(value[index])) {
+            continue;
+        }
+        if (index == ws_count) {
+            lws_count = ws_count;
+        } else if (ws_count > 1) {
+            iscanonical = 0;
+        }
+        ws_count = 0;
+
+        /* start of the item */
+        item = &value[index];
+        for (item_len = 0; index + item_len < value_len && !isspace(item[item_len]); item_len++);
+        LY_ARRAY_FOR(type_bits->bits, u) {
+            if (!strncmp(type_bits->bits[u].name, item, item_len) && type_bits->bits[u].name[item_len] == '\0') {
+                /* we have the match */
+                int inserted;
+
+                /* check that the bit is not disabled */
+                LY_ARRAY_FOR(type_bits->bits[u].iffeatures, v) {
+                    if (!lysc_iffeature_value(&type_bits->bits[u].iffeatures[v])) {
+                        asprintf(&errmsg, "Bit \"%s\" is disabled by its %u. if-feature condition.",
+                                 type_bits->bits[u].name, v + 1);
+                        goto error;
+                    }
+                }
+
+                if (iscanonical &&items->count && type_bits->bits[u].position < ((struct lysc_type_bitenum_item*)items->objs[items->count - 1])->position) {
+                    iscanonical = 0;
+                }
+                inserted = ly_set_add(items, &type_bits->bits[u], 0);
+                LY_CHECK_ERR_GOTO(inserted == -1, ret = LY_EMEM, error);
+                if ((unsigned int)inserted != items->count - 1) {
+                    asprintf(&errmsg, "Bit \"%s\" used multiple times.", type_bits->bits[u].name);
+                    goto error;
+                }
+                goto next;
+            }
+        }
+        /* item not found */
+        asprintf(&errmsg, "Invalid bit value \"%.*s\".", (int)item_len, item);
+        goto error;
+next:
+        /* remember for canonized form: item + space/termination-byte */
+        buf_size += item_len + 1;
+        index += item_len;
+    }
+    /* validation done */
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        if (options & LY_TYPE_OPTS_STORE) {
+            if (iscanonical) {
+                items_ordered = items;
+                items = NULL;
+            } else {
+                items_ordered = ly_set_dup(items, NULL);
+                LY_CHECK_ERR_GOTO(!items_ordered, LOGMEM(ctx); ret = LY_EMEM, error);
+                items_ordered->count = 0;
+            }
+        }
+        if (iscanonical) {
+            if (!ws_count && !lws_count && (options & LY_TYPE_OPTS_DYNAMIC)) {
+                *canonized = lydict_insert_zc(ctx, (char*)value);
+                value = NULL;
+            } else {
+                *canonized = lydict_insert(ctx, value_len ? &value[lws_count] : "", value_len - ws_count - lws_count);
+            }
+        } else {
+            buf = malloc(buf_size * sizeof *buf);
+            LY_CHECK_ERR_GOTO(!buf, LOGMEM(ctx); ret = LY_EMEM, error);
+            index = 0;
+
+            /* generate ordered bits list */
+            LY_ARRAY_FOR(type_bits->bits, u) {
+                int i = ly_set_contains(items, &type_bits->bits[u]);
+                if (i != -1) {
+                    int c = sprintf(&buf[index], "%s%s", index ? " " : "", type_bits->bits[u].name);
+                    LY_CHECK_ERR_GOTO(c < 0, LOGERR(ctx, LY_ESYS, "sprintf() failed."); ret = LY_ESYS, error);
+                    index += c;
+                    if (items_ordered) {
+                        ly_set_add(items_ordered, &type_bits->bits[u], LY_SET_OPT_USEASLIST);
+                    }
+                }
+            }
+            assert(buf_size == index + 1);
+            /* termination NULL-byte */
+            buf[index] = '\0';
+
+            *canonized = lydict_insert_zc(ctx, buf);
+            buf = NULL;
+        }
+    }
+
+    if (options & LY_TYPE_OPTS_STORE) {
+        /* remember the set to store */
+        if (items_ordered) {
+            *priv = items_ordered;
+        } else {
+            *priv = items;
+            items = NULL;
+        }
+    }
+
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+
+    ly_set_free(items, NULL);
+    return LY_SUCCESS;
+error:
+    if (errmsg) {
+        *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    }
+    ly_set_free(items, NULL);
+    ly_set_free(items_ordered, NULL);
+    free(buf);
+    return ret;
+}
+
+/**
+ * @brief Store value of the YANG built-in bits type.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_bits(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), int options,
+                   struct lyd_value *value, struct ly_err_item **UNUSED(err), void **priv)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct ly_set *items = NULL;
+    unsigned int u;
+
+    if (options & LY_TYPE_OPTS_VALIDATE) {
+        /* the value was prepared by ly_type_validate_bits() */
+        items = (struct ly_set *)(*priv);
+        LY_ARRAY_CREATE_GOTO(ctx, value->bits_items, items->count, ret, cleanup);
+        for (u = 0; u < items->count; u++) {
+            value->bits_items[u] = items->objs[u];
+            LY_ARRAY_INCREMENT(value->bits_items);
+        }
+    } else {
+        /* TODO if there is usecase for store without validate */
+        LOGINT(NULL);
+        return LY_EINT;
+    }
+
+cleanup:
+    ly_set_free(items, NULL);
+    return ret;
+}
+
+/**
+ * @brief Free value of the YANG built-in bits type.
+ *
+ * Implementation of the ly_type_free_clb.
+ */
+static void
+ly_type_free_bits(struct ly_ctx *UNUSED(ctx), struct lysc_type *UNUSED(type), struct lyd_value *value)
+{
+    LY_ARRAY_FREE(value->bits_items);
+    value->bits_items = NULL;
+}
+
+
+/**
+ * @brief Validate and canonize value of the YANG built-in enumeration type.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_enum(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                       const char **canonized, struct ly_err_item **err, void **priv)
+{
+    unsigned int u, v;
+    char *errmsg = NULL;
+    struct lysc_type_enum *type_enum = (struct lysc_type_enum*)type;
+
+    /* find the matching enumeration value item */
+    LY_ARRAY_FOR(type_enum->enums, u) {
+        if (!strncmp(type_enum->enums[u].name, value, value_len) && type_enum->enums[u].name[value_len] == '\0') {
+            /* we have the match */
+
+            /* check that the enumeration value is not disabled */
+            LY_ARRAY_FOR(type_enum->enums[u].iffeatures, v) {
+                if (!lysc_iffeature_value(&type_enum->enums[u].iffeatures[v])) {
+                    asprintf(&errmsg, "Enumeration \"%s\" is disabled by its %u. if-feature condition.",
+                             type_enum->enums[u].name, v + 1);
+                    goto error;
+                }
+            }
+            goto match;
+        }
+    }
+    /* enum not found */
+    asprintf(&errmsg, "Invalid enumeration value \"%.*s\".", (int)value_len, value);
+    goto error;
+
+match:
+    /* validation done */
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        if (options & LY_TYPE_OPTS_DYNAMIC) {
+            *canonized = lydict_insert_zc(ctx, (char*)value);
+            value = NULL;
+        } else {
+            *canonized = lydict_insert(ctx, value_len ? value : "", value_len);
+        }
+    }
+
+    if (options & LY_TYPE_OPTS_STORE) {
+        /* remember the enum definition to store */
+        *priv = &type_enum->enums[u];
+    }
+
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+
+    return LY_SUCCESS;
+
+error:
+    if (errmsg) {
+        *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    }
+    return LY_EVALID;
+}
+
+/**
+ * @brief Store value of the YANG built-in enumeration type.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_enum(struct ly_ctx *UNUSED(ctx), struct lysc_type *UNUSED(type), int options,
+                   struct lyd_value *value, struct ly_err_item **UNUSED(err), void **priv)
+{
+    if (options & LY_TYPE_OPTS_VALIDATE) {
+        /* the value was prepared by ly_type_validate_enum() */
+        value->enum_item = *priv;
+    } else {
+        /* TODO if there is usecase for store without validate */
+        LOGINT(NULL);
+        return LY_EINT;
+    }
+
+    return LY_SUCCESS;
+}
+
+struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
+    {0}, /* LY_TYPE_UNKNOWN */
+    {.type = LY_TYPE_BINARY, .validate = ly_type_validate_binary, .store = NULL, .free = NULL},
+    {.type = LY_TYPE_UINT8, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .free = NULL},
+    {.type = LY_TYPE_UINT16, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .free = NULL},
+    {.type = LY_TYPE_UINT32, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .free = NULL},
+    {.type = LY_TYPE_UINT64, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .free = NULL},
+    {.type = LY_TYPE_STRING, .validate = ly_type_validate_string, .store = NULL, .free = NULL},
+    {.type = LY_TYPE_BITS, .validate = ly_type_validate_bits, .store = ly_type_store_bits, .free = ly_type_free_bits},
+    {0}, /* TODO LY_TYPE_BOOL */
+    {.type = LY_TYPE_DEC64, .validate = ly_type_validate_decimal64, .store = ly_type_store_decimal64, .free = NULL},
+    {0}, /* TODO LY_TYPE_EMPTY */
+    {.type = LY_TYPE_ENUM, .validate = ly_type_validate_enum, .store = ly_type_store_enum, .free = NULL},
+    {0}, /* TODO LY_TYPE_IDENT */
+    {0}, /* TODO LY_TYPE_INST */
+    {0}, /* TODO LY_TYPE_LEAFREF */
+    {0}, /* TODO LY_TYPE_UNION */
+    {.type = LY_TYPE_INT8, .validate = ly_type_validate_int, .store = ly_type_store_int, .free = NULL},
+    {.type = LY_TYPE_INT16, .validate = ly_type_validate_int, .store = ly_type_store_int, .free = NULL},
+    {.type = LY_TYPE_INT32, .validate = ly_type_validate_int, .store = ly_type_store_int, .free = NULL},
+    {.type = LY_TYPE_INT64, .validate = ly_type_validate_int, .store = ly_type_store_int, .free = NULL},
+};
+
diff --git a/src/plugins_types.h b/src/plugins_types.h
new file mode 100644
index 0000000..9c48fd8
--- /dev/null
+++ b/src/plugins_types.h
@@ -0,0 +1,219 @@
+/**
+ * @file plugins_types.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief API for (user) types plugins
+ *
+ * Copyright (c) 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
+ */
+
+#ifndef LY_PLUGINS_TYPES_H_
+#define LY_PLUGINS_TYPES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "log.h"
+#include "tree.h"
+#include "tree_schema.h"
+#include "tree_data.h"
+
+/**
+ * @brief Helper function for various plugin functions to generate error information structure.
+ *
+ * @param[in] level Error level of the error.
+ * @param[in] code Code of the error.
+ * @param[in] vecode Validity error code in case of LY_EVALID error code.
+ * @param[in] msg Error message.
+ * @param[in] path Path to the node causing the error.
+ * @param[in] apptag Error-app-tag value.
+ * @return NULL in case of memory allocation failure.
+ * @return Created error information structure that can be freed using ly_err_free().
+ */
+struct ly_err_item *ly_err_new(LY_LOG_LEVEL level, LY_ERR code, LY_VECODE vecode, char *msg, char *path, char *apptag);
+
+/**
+ * @brief Destructor for the error records created with ly_err_new().
+ *
+ * Compatible with the free(), so usable as a generic callback.
+ *
+ * @param[in] ptr Error record to free. With the record, also all the records connected after this one are freed.
+ */
+void ly_err_free(void *ptr);
+
+/**
+ * @defgroup plugintypeopts Options for type plugin callbacks. The same set of the options is passed to all the type's callbacks used together.
+ *
+ * Options applicable to ly_type_validate_clb() and ly_typestore_clb.
+ * @{
+ */
+#define LY_TYPE_OPTS_VALIDATE     0x01 /**< Flag announcing calling of ly_type_validate_clb() of the type */
+#define LY_TYPE_OPTS_CANONIZE     0x02 /**< Canonize the given value and store it (insert into the context's dictionary)
+                                            as the value's canonized string */
+#define LY_TYPE_OPTS_DYNAMIC      0x04 /**< Flag for the dynamically allocated string value, in this case the value is supposed to be freed
+                                            or directly inserted into the context's dictionary (e.g. in case of canonization).
+                                            In any case, the caller of the callback does not free the provided string value after calling
+                                            the type's callbacks with this option */
+#define LY_TYPE_OPTS_STORE        0x08 /**< Flag announcing calling of ly_type_store_clb() */
+#define LY_TYPE_OPTS_SCHEMA       0x10 /**< Flag for the value used in schema instead of the data tree */
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup plugintypeflags Various flags for type plugins usage.
+ * Options applicable to ly_type_validate_clb().
+ * @{
+ */
+#define LY_TYPE_FLAG_PREFIXES 0x01     /**< The value contains prefixes, so when printing XML,
+                                            get the namespaces connected with the prefixes and print them. */
+/**
+ * @}
+ */
+
+/**
+ * @brief Callback to validate the given @p value according to the given @p type. Optionaly, it can be requested to canonize the value.
+ *
+ * Note that the \p value string is not necessarily zero-terminated. The provided \p value_len is always correct.
+ *
+ * @param[in] ctx libyang Context
+ * @param[in] type Type of the value being canonized.
+ * @param[in] value Lexical representation of the value to be validated (and canonized).
+ * @param[in] value_len Length of the given \p value.
+ * @param[in] options [Type plugin options ](@ref plugintypeopts).
+ * @param[out] canonized If LY_TYPE_VALIDATE_CANONIZE option set, the canonized string stored in the @p ctx dictionary is returned via this parameter.
+ * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic error message is prepared instead.
+ * The error structure can be created by ly_err_new().
+ * @param[out] priv Type's private data passed between all the callbacks. The last callback is supposed to free the data allocated beforehand.
+ * @return LY_SUCCESS on success
+ * @return LY_ERR value if an error occurred and the value could not be canonized following the type's rules.
+ */
+typedef LY_ERR (*ly_type_validate_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                                       const char **canonized, struct ly_err_item **err, void **priv);
+
+/**
+ * @brief Callback for storing user type values.
+ *
+ * @param[in] ctx libyang ctx to enable correct manipulation with values that are in the dictionary.
+ * @param[in] type Type of the value being stored.
+ * @param[in] options [Type plugin options ](@ref plugintypeopts).
+ * @param[in,out] value Value structure to store the data in the type's specific way. The structure already contains canonized value string to be processed.
+ * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic error message is prepared instead.
+ * The error structure can be created by ly_err_new().
+ * @param[out] priv Type's private data passed between all the callbacks. The last callback is supposed to free the data allocated beforehand.
+ * @return LY_SUCCESS on success
+ * @return LY_ERR value if an error occurred and the value could not be stored for any reason.
+ */
+typedef LY_ERR (*ly_type_store_clb)(struct ly_ctx *ctx, struct lysc_type *type, int options,
+                                    struct lyd_value *value, struct ly_err_item **err, void **priv);
+
+/**
+ * @brief Callback for freeing the user type values stored by ly_type_store_clb().
+ *
+ *
+ * @param[in] ctx libyang ctx to enable correct manipulation with values that are in the dictionary.
+ * @param[in] type Type of the stored value.
+ * @param[in,out] value Value structure to free the data stored there by the plugin's ly_type_store_clb() callback
+ */
+typedef void (*ly_type_free_clb)(struct ly_ctx *ctx, struct lysc_type *type, struct lyd_value *value);
+
+/**
+ * @brief Hold type-specific functions for various operations with the data values.
+ *
+ * libyang includes set of plugins for all the built-in types. They are, by default, inherited to the derived types.
+ * However, if the user type plugin for the specific type is loaded, the plugin can provide it's own functions.
+ * The built-in types plugins and are public, so even the user type plugins can use them to do part of their own functionality.
+ */
+struct lysc_type_plugin {
+    LY_DATA_TYPE type;               /**< implemented type, use LY_TYPE_UNKNOWN for derived data types */
+    ly_type_validate_clb validate;   /**< function to validate and canonize given value */
+    ly_type_store_clb store;         /**< function to store the value in the type-specific way */
+    ly_type_free_clb free;           /**< function to free the type-spceific way stored value */
+    uint32_t flags;                  /**< [type flags ](@ref plugintypeflags). */
+};
+
+/**
+ * @brief List of type plugins for built-in types.
+ */
+extern struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT];
+
+/**
+ * @brief Unsigned integer value parser and validator.
+ *
+ * @param[in] datatype Type of the integer for logging.
+ * @param[in] base Base of the integer's lexical representation. In case of built-in types, data must be represented in decimal format (base 10),
+ * but default values in schemas can be represented also as hexadecimal or octal values (base 0).
+ * @param[in] min Lower bound of the type.
+ * @param[in] max Upper bound of the type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed integer value (optional).
+ * @param[out] err Error information in case of failure. The error structure can be freed by ly_err_free().
+ * @return LY_ERR value according to the result of the parsing and validation.
+ */
+LY_ERR parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len,
+                 int64_t *ret, struct ly_err_item **err);
+
+/**
+ * @brief Unsigned integer value parser and validator.
+ *
+ * @param[in] datatype Type of the unsigned integer for logging.
+ * @param[in] base Base of the integer's lexical representation. In case of built-in types, data must be represented in decimal format (base 10),
+ * but default values in schemas can be represented also as hexadecimal or octal values (base 0).
+ * @param[in] min Lower bound of the type.
+ * @param[in] max Upper bound of the type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed unsigned integer value (optional).
+ * @param[out] err Error information in case of failure. The error structure can be freed by ly_err_free().
+ * @return LY_ERR value according to the result of the parsing and validation.
+ */
+LY_ERR parse_uint(const char *datatype, int base, uint64_t min, uint64_t max, const char *value, size_t value_len,
+                  uint64_t *ret, struct ly_err_item **err);
+
+/**
+ * @brief Convert a string with a decimal64 value into libyang representation:
+ * ret = value * 10^fraction-digits
+ *
+ * @param[in] fraction_digits Fraction-digits of the decimal64 type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed decimal64 value representing original value * 10^fraction-digits (optional).
+ * @param[out] err Error information in case of failure. The error structure can be freed by ly_err_free().
+ * @return LY_ERR value according to the result of the parsing and validation.
+ */
+LY_ERR parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err);
+
+/**
+ * @brief Data type validator for a range/length-restricted values.
+ *
+ * @param[in] basetype Base built-in type of the type with the range specified to get know if the @p range structure represents range or length restriction.
+ * @param[in] range Range (length) restriction information.
+ * @param[in] value Value to check. In case of basetypes using unsigned integer values, the value is actually cast to uint64_t.
+ * @param[in] canonized Canonized @p value for error logging.
+ * @param[out] err Error information in case of failure. The error structure can be freed by ly_err_free().
+ * @return LY_ERR value according to the result of the validation.
+ */
+LY_ERR ly_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *canonized, struct ly_err_item **err);
+
+/**
+ * @brief Data type validator for pattern-restricted string values.
+ *
+ * @param[in] patterns ([Sized array](@ref sizedarrays)) of the compiled list of pointers to the pattern restrictions.
+ * The array can be found in the lysc_type_str::patterns structure.
+ * @param[in] str String to validate.
+ * @param[in] str_len Length of the string to validate (mandatory).
+ * @param[out] err Error information in case of failure or non-matching @p str. The error structure can be freed by ly_err_free().
+ * @return LY_SUCCESS when @p matches all the patterns.
+ * @return LY_EVALID when @p does not match any of the patterns.
+ * @return LY_ESYS in case of PCRE2 error.
+ */
+LY_ERR ly_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len, struct ly_err_item **err);
+
+#endif /* LY_PLUGINS_TYPES_H_ */
diff --git a/src/printer.c b/src/printer.c
new file mode 100644
index 0000000..72872b1
--- /dev/null
+++ b/src/printer.c
@@ -0,0 +1,342 @@
+/**
+ * @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
+ */
+
+#include "common.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "printer_internal.h"
+
+/**
+ * @brief informational structure shared by printers
+ */
+struct ext_substmt_info_s ext_substmt_info[] = {
+  {NULL, NULL, 0},                              /**< LYEXT_SUBSTMT_SELF */
+  {"argument", "name", SUBST_FLAG_ID},          /**< LYEXT_SUBSTMT_ARGUMENT */
+  {"base", "name", SUBST_FLAG_ID},              /**< LYEXT_SUBSTMT_BASE */
+  {"belongs-to", "module", SUBST_FLAG_ID},      /**< LYEXT_SUBSTMT_BELONGSTO */
+  {"contact", "text", SUBST_FLAG_YIN},          /**< LYEXT_SUBSTMT_CONTACT */
+  {"default", "value", 0},                      /**< LYEXT_SUBSTMT_DEFAULT */
+  {"description", "text", SUBST_FLAG_YIN},      /**< LYEXT_SUBSTMT_DESCRIPTION */
+  {"error-app-tag", "value", 0},                /**< LYEXT_SUBSTMT_ERRTAG */
+  {"error-message", "value", SUBST_FLAG_YIN},   /**< LYEXT_SUBSTMT_ERRMSG */
+  {"key", "value", 0},                          /**< LYEXT_SUBSTMT_KEY */
+  {"namespace", "uri", 0},                      /**< LYEXT_SUBSTMT_NAMESPACE */
+  {"organization", "text", SUBST_FLAG_YIN},     /**< LYEXT_SUBSTMT_ORGANIZATION */
+  {"path", "value", 0},                         /**< LYEXT_SUBSTMT_PATH */
+  {"prefix", "value", SUBST_FLAG_ID},           /**< LYEXT_SUBSTMT_PREFIX */
+  {"presence", "value", 0},                     /**< LYEXT_SUBSTMT_PRESENCE */
+  {"reference", "text", SUBST_FLAG_YIN},        /**< LYEXT_SUBSTMT_REFERENCE */
+  {"revision-date", "date", SUBST_FLAG_ID},     /**< LYEXT_SUBSTMT_REVISIONDATE */
+  {"units", "name", 0},                         /**< LYEXT_SUBSTMT_UNITS */
+  {"value", "value", SUBST_FLAG_ID},            /**< LYEXT_SUBSTMT_VALUE */
+  {"yang-version", "value", SUBST_FLAG_ID},     /**< LYEXT_SUBSTMT_VERSION */
+  {"modifier", "value", SUBST_FLAG_ID},         /**< LYEXT_SUBSTMT_MODIFIER */
+  {"require-instance", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REQINST */
+  {"yin-element", "value", SUBST_FLAG_ID},      /**< LYEXT_SUBSTMT_YINELEM */
+  {"config", "value", SUBST_FLAG_ID},           /**< LYEXT_SUBSTMT_CONFIG */
+  {"mandatory", "value", SUBST_FLAG_ID},        /**< LYEXT_SUBSTMT_MANDATORY */
+  {"ordered-by", "value", SUBST_FLAG_ID},       /**< LYEXT_SUBSTMT_ORDEREDBY */
+  {"status", "value", SUBST_FLAG_ID},           /**< LYEXT_SUBSTMT_STATUS */
+  {"fraction-digits", "value", SUBST_FLAG_ID},  /**< LYEXT_SUBSTMT_DIGITS */
+  {"max-elements", "value", SUBST_FLAG_ID},     /**< LYEXT_SUBSTMT_MAX */
+  {"min-elements", "value", SUBST_FLAG_ID},     /**< LYEXT_SUBSTMT_MIN */
+  {"position", "value", SUBST_FLAG_ID},         /**< LYEXT_SUBSTMT_POSITION */
+  {"unique", "tag", 0},                         /**< LYEXT_SUBSTMT_UNIQUE */
+};
+
+LY_ERR
+ly_print(struct lyout *out, const char *format, ...)
+{
+    int count = 0;
+    char *msg = NULL, *aux;
+    va_list ap;
+#ifndef HAVE_VDPRINTF
+    int fd;
+    FILE *stream;
+#endif
+
+    LYOUT_CHECK(out, out->status);
+
+    va_start(ap, format);
+
+    switch (out->type) {
+    case LYOUT_FD:
+#ifdef HAVE_VDPRINTF
+        count = vdprintf(out->method.fd, format, ap);
+        break;
+#else
+        /* Without vdfprintf(), change the printing method to printing to a FILE stream.
+         * To preserve the original file descriptor, duplicate it and use it to open file stream.
+         * Due to a standalone LYOUT_FDSTREAM, ly*_print_fd() functions are supposed to detect the
+         * change and close the stream on their exit. */
+        fd = dup(out->method.fd);
+        if (fd < 0) {
+            LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
+                   out->method.fd, strerror(errno));
+            va_end(ap);
+            out->status = LY_ESYS;
+            return LY_ESYS;
+        }
+        stream = fdopen(fd, "a");
+        if (!stream) {
+            LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
+                   out->method.fd, strerror(errno));
+            va_end(ap);
+            out->status = LY_ESYS;
+            return LY_ESYS;
+        }
+        out->method.f = stream;
+        out->type = LYOUT_FDSTREAM;
+#endif
+        /* fall through */
+    case LYOUT_FDSTREAM:
+    case LYOUT_STREAM:
+        count = vfprintf(out->method.f, format, ap);
+        break;
+    case LYOUT_MEMORY:
+        if ((count = vasprintf(&msg, format, ap)) < 0) {
+            break;
+        }
+        if (out->method.mem.len + count + 1 > out->method.mem.size) {
+            aux = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
+            if (!aux) {
+                out->method.mem.buf = NULL;
+                out->method.mem.len = 0;
+                out->method.mem.size = 0;
+                LOGMEM(NULL);
+                va_end(ap);
+                return LY_EMEM;
+            }
+            out->method.mem.buf = aux;
+            out->method.mem.size = out->method.mem.len + count + 1;
+        }
+        memcpy(&out->method.mem.buf[out->method.mem.len], msg, count);
+        out->method.mem.len += count;
+        out->method.mem.buf[out->method.mem.len] = '\0';
+        free(msg);
+        break;
+    case LYOUT_CALLBACK:
+        if ((count = vasprintf(&msg, format, ap)) < 0) {
+            break;
+        }
+        count = out->method.clb.f(out->method.clb.arg, msg, count);
+        free(msg);
+        break;
+    }
+
+    va_end(ap);
+
+    if (count < 0) {
+        LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
+        out->status = LY_ESYS;
+        return LY_ESYS;
+    } else {
+        out->printed += count;
+        return LY_SUCCESS;
+    }
+}
+
+void
+ly_print_flush(struct lyout *out)
+{
+    switch (out->type) {
+    case LYOUT_FDSTREAM:
+    case LYOUT_STREAM:
+        fflush(out->method.f);
+        break;
+    case LYOUT_FD:
+        fsync(out->method.fd);
+        break;
+    case LYOUT_MEMORY:
+    case LYOUT_CALLBACK:
+        /* nothing to do */
+        break;
+    }
+
+    free(out->buffered);
+    out->buf_size = out->buf_len = 0;
+}
+
+LY_ERR
+ly_write(struct lyout *out, const char *buf, size_t len)
+{
+    int written = 0;
+
+    LYOUT_CHECK(out, out->status);
+
+    if (out->hole_count) {
+        /* we are buffering data after a hole */
+        if (out->buf_len + len > out->buf_size) {
+            out->buffered = ly_realloc(out->buffered, out->buf_len + len);
+            if (!out->buffered) {
+                out->buf_len = 0;
+                out->buf_size = 0;
+                LOGMEM_RET(NULL);
+            }
+            out->buf_size = out->buf_len + len;
+        }
+
+        memcpy(&out->buffered[out->buf_len], buf, len);
+        out->buf_len += len;
+        return LY_SUCCESS;
+    }
+
+repeat:
+    switch (out->type) {
+    case LYOUT_MEMORY:
+        if (out->method.mem.len + len + 1 > out->method.mem.size) {
+            out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + len + 1);
+            if (!out->method.mem.buf) {
+                out->method.mem.len = 0;
+                out->method.mem.size = 0;
+                LOGMEM_RET(NULL);
+            }
+            out->method.mem.size = out->method.mem.len + len + 1;
+        }
+        memcpy(&out->method.mem.buf[out->method.mem.len], buf, len);
+        out->method.mem.len += len;
+        out->method.mem.buf[out->method.mem.len] = '\0';
+
+        out->printed += len;
+        return LY_SUCCESS;
+    case LYOUT_FD:
+        written = write(out->method.fd, buf, len);
+        break;
+    case LYOUT_FDSTREAM:
+    case LYOUT_STREAM:
+        written =  fwrite(buf, sizeof *buf, len, out->method.f);
+        break;
+    case LYOUT_CALLBACK:
+        written = out->method.clb.f(out->method.clb.arg, buf, len);
+        break;
+    }
+
+    if (written < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            goto repeat;
+        }
+        LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
+        out->status = LY_ESYS;
+        return LY_ESYS;
+    } else if ((size_t)written != len) {
+        LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__, len - (size_t)written, len);
+        out->status = LY_ESYS;
+        return LY_ESYS;
+    } else {
+        out->printed += written;
+        return LY_SUCCESS;
+    }
+}
+
+LY_ERR
+ly_write_skip(struct lyout *out, size_t count, size_t *position)
+{
+    LYOUT_CHECK(out, out->status);
+
+    switch (out->type) {
+    case LYOUT_MEMORY:
+        if (out->method.mem.len + count > out->method.mem.size) {
+            out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count);
+            if (!out->method.mem.buf) {
+                out->method.mem.len = 0;
+                out->method.mem.size = 0;
+                out->status = LY_ESYS;
+                LOGMEM_RET(NULL);
+            }
+            out->method.mem.size = out->method.mem.len + count;
+        }
+
+        /* save the current position */
+        *position = out->method.mem.len;
+
+        /* skip the memory */
+        out->method.mem.len += count;
+
+        /* update printed bytes counter despite we actually printed just a hole */
+        out->printed += count;
+        break;
+    case LYOUT_FD:
+    case LYOUT_FDSTREAM:
+    case LYOUT_STREAM:
+    case LYOUT_CALLBACK:
+        /* buffer the hole */
+        if (out->buf_len + count > out->buf_size) {
+            out->buffered = ly_realloc(out->buffered, out->buf_len + count);
+            if (!out->buffered) {
+                out->buf_len = 0;
+                out->buf_size = 0;
+                out->status = LY_ESYS;
+                LOGMEM_RET(NULL);
+            }
+            out->buf_size = out->buf_len + count;
+        }
+
+        /* save the current position */
+        *position = out->buf_len;
+
+        /* skip the memory */
+        out->buf_len += count;
+
+        /* increase hole counter */
+        ++out->hole_count;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
+{
+    LY_ERR ret = LY_SUCCESS;
+
+    LYOUT_CHECK(out, out->status);
+
+    switch (out->type) {
+    case LYOUT_MEMORY:
+        /* write */
+        memcpy(&out->method.mem.buf[position], buf, count);
+        break;
+    case LYOUT_FD:
+    case LYOUT_FDSTREAM:
+    case LYOUT_STREAM:
+    case LYOUT_CALLBACK:
+        if (out->buf_len < position + count) {
+            out->status = LY_ESYS;
+            LOGMEM_RET(NULL);
+        }
+
+        /* write into the hole */
+        memcpy(&out->buffered[position], buf, count);
+
+        /* decrease hole counter */
+        --out->hole_count;
+
+        if (!out->hole_count) {
+            /* all holes filled, we can write the buffer,
+             * printed bytes counter is updated by ly_write() */
+            ret = ly_write(out, out->buffered, out->buf_len);
+            out->buf_len = 0;
+        }
+        break;
+    }
+
+    return ret;
+}
diff --git a/src/printer_data.c b/src/printer_data.c
new file mode 100644
index 0000000..b74eaee
--- /dev/null
+++ b/src/printer_data.c
@@ -0,0 +1,194 @@
+/**
+ * @file printer_data.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic data 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
+ */
+
+#include "common.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "printer_internal.h"
+#include "tree_schema.h"
+#include "tree_data.h"
+
+/**
+ * @brief Common YANG data printer.
+ *
+ * @param[in] out Prepared structure defining the type and details of the printer output.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] format Output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags). With \p format LYD_LYB, only #LYP_WITHSIBLINGS option is accepted.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_print_(struct lyout *out, const struct lyd_node *root, LYD_FORMAT format, int options)
+{
+    LY_ERR ret;
+
+    switch (format) {
+    case LYD_XML:
+        ret = xml_print_data(out, root, options);
+        break;
+#if 0
+    case LYD_JSON:
+        ret = json_print_data(out, root, options);
+        break;
+    case LYD_LYB:
+        ret = lyb_print_data(out, root, options);
+        break;
+#endif
+    default:
+        LOGERR(out->ctx, LY_EINVAL, "Unknown output format.");
+        ret = LY_EINVAL;
+        break;
+    }
+
+    return ret;
+}
+
+API ssize_t
+lyd_print_file(FILE *f, const struct lyd_node *root, LYD_FORMAT format, int options)
+{
+    struct lyout out;
+    LY_ERR ret;
+
+    LY_CHECK_ARG_RET(NULL, f, LY_EINVAL);
+
+    memset(&out, 0, sizeof out);
+    out.type = LYOUT_STREAM;
+    out.method.f = f;
+
+    if (root) {
+        out.ctx = root->schema->module->ctx;
+    }
+
+    ret = lyd_print_(&out, root, format, options);
+    if (ret) {
+        /* error */
+        return (-1) * ret;
+    } else {
+        /* success */
+        return (ssize_t)out.printed;
+    }
+}
+
+API ssize_t
+lyd_print_path(const char *path, const struct lyd_node *root, LYD_FORMAT format, int options)
+{
+    FILE *f;
+    ssize_t ret;
+
+    LY_CHECK_ARG_RET(NULL, path, LY_EINVAL);
+
+    f = fopen(path, "w");
+    if (!f) {
+        LOGERR(root ? root->schema->module->ctx : NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", path, strerror(errno));
+        return LY_ESYS;
+    }
+
+    ret = lyd_print_file(f, root, format, options);
+    fclose(f);
+    return ret;
+}
+
+API ssize_t
+lyd_print_fd(int fd, const struct lyd_node *root, LYD_FORMAT format, int options)
+{
+    LY_ERR ret;
+    struct lyout out;
+
+    LY_CHECK_ARG_RET(NULL, fd >= 0, LY_EINVAL);
+
+    memset(&out, 0, sizeof out);
+    out.type = LYOUT_FD;
+    out.method.fd = fd;
+
+    if (root) {
+        out.ctx = root->schema->module->ctx;
+    }
+
+    ret = lyd_print_(&out, root, format, options);
+
+    if (out.type == LYOUT_FDSTREAM) {
+        /* close temporary stream based on the given file descriptor */
+        fclose(out.method.f);
+        /* move the original file descriptor to the end of the output file */
+        lseek(fd, 0, SEEK_END);
+    }
+
+    if (ret) {
+        /* error */
+        return (-1) * ret;
+    } else {
+        /* success */
+        return (ssize_t)out.printed;
+    }
+}
+
+API ssize_t
+lyd_print_mem(char **strp, const struct lyd_node *root, LYD_FORMAT format, int options)
+{
+    struct lyout out;
+    LY_ERR ret;
+
+    LY_CHECK_ARG_RET(NULL, strp, LY_EINVAL);
+
+    memset(&out, 0, sizeof out);
+    out.type = LYOUT_MEMORY;
+
+    if (root) {
+        out.ctx = root->schema->module->ctx;
+    }
+
+    ret = lyd_print_(&out, root, format, options);
+    if (ret) {
+        /* error */
+        *strp = NULL;
+        return (-1) * ret;
+    } else {
+        /* success */
+        *strp = out.method.mem.buf;
+        return (ssize_t)out.printed;
+    }
+}
+
+API ssize_t
+lyd_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lyd_node *root,
+              LYD_FORMAT format, int options)
+{
+    LY_ERR ret;
+    struct lyout out;
+
+    LY_CHECK_ARG_RET(NULL, writeclb, LY_EINVAL);
+
+    memset(&out, 0, sizeof out);
+    out.type = LYOUT_CALLBACK;
+    out.method.clb.f = writeclb;
+    out.method.clb.arg = arg;
+
+    if (root) {
+        out.ctx = root->schema->module->ctx;
+    }
+
+    ret = lyd_print_(&out, root, format, options);
+    if (ret) {
+        /* error */
+        return (-1) * ret;
+    } else {
+        /* success */
+        return (ssize_t)out.printed;
+    }
+}
diff --git a/src/printer_data.h b/src/printer_data.h
new file mode 100644
index 0000000..197463c
--- /dev/null
+++ b/src/printer_data.h
@@ -0,0 +1,125 @@
+/**
+ * @file printer_schema.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema printers for libyang
+ *
+ * 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
+ */
+
+#ifndef LY_PRINTER_DATA_H_
+#define LY_PRINTER_DATA_H_
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "tree_data.h"
+
+/**
+ * @defgroup dataprinterflags Data printer flags
+ * @ingroup datatree
+ *
+ * Validity flags for data nodes.
+ *
+ * @{
+ */
+#define LYDP_WITHSIBLINGS  0x01  /**< Flag for printing also the (following) sibling nodes of the data node. */
+#define LYDP_FORMAT        0x02  /**< Flag for formatted output. */
+#define LYDP_KEEPEMPTYCONT 0x04  /**< Preserve empty non-presence containers */
+#define LYDP_WD_MASK       0xF0  /**< Mask for with-defaults modes */
+#define LYDP_WD_EXPLICIT   0x00  /**< Explicit mode - print only data explicitly being present in the data tree.
+                                      Note that this is the default value when no WD option is specified. */
+#define LYDP_WD_TRIM       0x10  /**< Do not print the nodes with the value equal to their default value */
+#define LYDP_WD_ALL        0x20  /**< Include implicit default nodes */
+#define LYDP_WD_ALL_TAG    0x40  /**< Same as #LYP_WD_ALL but also adds attribute 'default' with value 'true' to
+                                      all nodes that has its default value. The 'default' attribute has namespace:
+                                      urn:ietf:params:xml:ns:netconf:default:1.0 and thus the attributes are
+                                      printed only when the ietf-netconf-with-defaults module is present in libyang
+                                      context (but in that case this namespace is always printed). */
+#define LYDP_WD_IMPL_TAG   0x80  /**< Same as LYP_WD_ALL_TAG but the attributes are added only to the nodes that
+                                      are not explicitly present in the original data tree despite their
+                                      value is equal to their default value.  There is the same limitation regarding
+                                      the presence of ietf-netconf-with-defaults module in libyang context. */
+#define LYDP_NETCONF       0x100 /**< Print the data tree for use in NETCONF meaning:
+                                      - for RPC output - skip the top-level RPC node,
+                                      - for action output - skip all the parents of and the action node itself,
+                                      - for action input - enclose the data in an action element in the base YANG namespace,
+                                      - for all other data - print the whole data tree normally. */
+/**
+ * @}
+ */
+
+/**
+ * @brief Print data tree in the specified format into a memory block.
+ * It is up to caller to free the returned string by free().
+ *
+ * @param[out] strp Pointer to store the resulting dump.
+ * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
+ * node of the data tree to print the specific subtree.
+ * @param[in] format Data output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags). With \p format LYD_LYB, only #LYP_WITHSIBLINGS option is accepted.
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lyd_print_mem(char **strp, const struct lyd_node *root, LYD_FORMAT format, int options);
+
+/**
+ * @brief Print data tree in the specified format into a file descriptor.
+ *
+ * @param[in] fd File descriptor where to print the data.
+ * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
+ * node of the data tree to print the specific subtree.
+ * @param[in] format Data output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags). With \p format LYD_LYB, only #LYP_WITHSIBLINGS option is accepted.
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lyd_print_fd(int fd, const struct lyd_node *root, LYD_FORMAT format, int options);
+
+/**
+ * @brief Print data tree in the specified format into a file stream.
+ *
+ * @param[in] f File stream where to print the schema.
+ * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
+ * node of the data tree to print the specific subtree.
+ * @param[in] format Data output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags). With \p format LYD_LYB, only #LYP_WITHSIBLINGS option is accepted.
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lyd_print_file(FILE *f, const struct lyd_node *root, LYD_FORMAT format, int options);
+
+/**
+ * @brief Print data tree in the specified format into a file.
+ *
+ * @param[in] path File where to print the schema.
+ * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
+ * node of the data tree to print the specific subtree.
+ * @param[in] format Data output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags). With \p format LYD_LYB, only #LYP_WITHSIBLINGS option is accepted.
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lyd_print_path(const char *path, const struct lyd_node *root, LYD_FORMAT format, int options);
+
+/**
+ * @brief Print data tree in the specified format using the provided callback.
+ *
+ * @param[in] writeclb Callback function to write the data (see write(2)).
+ * @param[in] arg Optional caller-specific argument to be passed to the \p writeclb callback.
+ * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
+ * node of the data tree to print the specific subtree.
+ * @param[in] format Data output format.
+ * @param[in] options [Data printer flags](@ref dataprinterflags). With \p format LYD_LYB, only #LYP_WITHSIBLINGS option is accepted.
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lyd_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lyd_node *root,
+                      LYD_FORMAT format, int options);
+
+#endif /* LY_PRINTER_DATA_H_ */
diff --git a/src/printer_internal.h b/src/printer_internal.h
new file mode 100644
index 0000000..7d0e312
--- /dev/null
+++ b/src/printer_internal.h
@@ -0,0 +1,169 @@
+/**
+ * @file printer_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Internal structures and functions for libyang
+ *
+ * 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
+ */
+
+#ifndef LY_PRINTER_INTERNAL_H_
+#define LY_PRINTER_INTERNAL_H_
+
+#include "printer_schema.h"
+#include "printer_data.h"
+
+/**
+ * @brief Types of the printer's output
+ */
+typedef enum LYOUT_TYPE {
+    LYOUT_FD,          /**< file descriptor */
+    LYOUT_STREAM,      /**< FILE stream */
+    LYOUT_FDSTREAM,    /**< FILE stream based on duplicated file descriptor */
+    LYOUT_MEMORY,      /**< memory */
+    LYOUT_CALLBACK     /**< print via provided callback */
+} LYOUT_TYPE;
+
+/**
+ * @brief Printer output structure specifying where the data are printed.
+ */
+struct lyout {
+    LYOUT_TYPE type;     /**< type of the output to select the output method */
+    union {
+        int fd;          /**< file descriptor for LYOUT_FD type */
+        FILE *f;         /**< file structure for LYOUT_STREAM and LYOUT_FDSTREAM types */
+        struct {
+            char *buf;        /**< pointer to the memory buffer to store the output */
+            size_t len;       /**< number of used bytes in the buffer */
+            size_t size;      /**< allocated size of the buffer */
+        } mem;           /**< memory buffer information for LYOUT_MEMORY type */
+        struct {
+            ssize_t (*f)(void *arg, const void *buf, size_t count); /**< callback function */
+            void *arg;        /**< optional argument for the callback function */
+        } clb;           /**< printer callback for LYOUT_CALLBACK type */
+    } method;            /**< type-specific information about the output */
+
+    char *buffered;      /**< additional buffer for holes, used only for LYB data format */
+    size_t buf_len;      /**< number of used bytes in the additional buffer for holes, used only for LYB data format */
+    size_t buf_size;     /**< allocated size of the buffer for holes, used only for LYB data format */
+    size_t hole_count;   /**< hole counter, used only for LYB data format */
+
+    size_t printed;      /**< Number of printed bytes */
+
+    struct ly_ctx *ctx;  /**< libyang context for error logging */
+    LY_ERR status;       /**< current status of the printer */
+};
+
+/**
+ * @brief Informational structure for YANG statements
+ */
+struct ext_substmt_info_s {
+    const char *name;      /**< name of the statement */
+    const char *arg;       /**< name of YIN's attribute to present the statement */
+    int flags;             /**< various flags to clarify printing of the statement */
+#define SUBST_FLAG_YIN 0x1 /**< has YIN element */
+#define SUBST_FLAG_ID 0x2  /**< the value is identifier -> no quotes */
+};
+
+/* filled in printer.c */
+extern struct ext_substmt_info_s ext_substmt_info[];
+
+/**
+ * @brief macro to check current status of the printer.
+ */
+#define LYOUT_CHECK(LYOUT, ...) if (LYOUT->status) {return __VA_ARGS__;}
+
+/**
+ * @brief YANG printer of the parsed schemas. Full YANG printer.
+ *
+ * @param[in] out Output specification.
+ * @param[in] module Schema to be printed (the parsed member is used).
+ * @return LY_ERR value, number of the printed bytes is updated in lyout::printed.
+ */
+LY_ERR yang_print_parsed(struct lyout *out, const struct lys_module *module);
+
+/**
+ * @brief YANG printer of the compiled schemas.
+ *
+ * This printer provides information about modules how they are understood by libyang.
+ * Despite the format is inspired by YANG, it is not fully compatible and should not be
+ * used as a standard YANG format.
+ *
+ * @param[in] out Output specification.
+ * @param[in] module Schema to be printed (the compiled member is used).
+ * @return LY_ERR value, number of the printed bytes is updated in lyout::printed.
+ */
+LY_ERR yang_print_compiled(struct lyout *out, const struct lys_module *module);
+
+/**
+ * @brief XML printer of the YANG data.
+ *
+ * @param[in] out Output specification.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in lyout::printed.
+ */
+LY_ERR xml_print_data(struct lyout *out, const struct lyd_node *root, int options);
+
+/**
+ * @brief Generic printer of the given format string into the specified output.
+ *
+ * Alternatively, ly_write() can be used.
+ *
+ * @param[in] out Output specification.
+ * @param[in] format format string to be printed.
+ * @return LY_ERR value, number of the printed bytes is updated in lyout::printed.
+ */
+LY_ERR ly_print(struct lyout *out, const char *format, ...);
+
+/**
+ * @brief Flush the output from any internal buffers and clean any auxiliary data.
+ * @param[in] out Output specification.
+ */
+void ly_print_flush(struct lyout *out);
+
+/**
+ * @brief Generic printer of the given string buffer into the specified output.
+ *
+ * Alternatively, ly_print() can be used.
+ *
+ * As an extension for printing holes (skipping some data until they are known),
+ * ly_write_skip() and ly_write_skipped() can be used.
+ *
+ * @param[in] out Output specification.
+ * @param[in] buf Memory buffer with the data to print.
+ * @param[in] len Length of the data to print in the @p buf.
+ * @return LY_ERR value, number of the printed bytes is updated in lyout::printed.
+ */
+LY_ERR ly_write(struct lyout *out, const char *buf, size_t len);
+
+/**
+ * @brief Create a hole in the output data that will be filled later.
+ *
+ * @param[in] out Output specification.
+ * @param[in] len Length of the created hole.
+ * @param[out] position Position of the hole, value must be later provided to the ly_write_skipped() call.
+ * @return LY_ERR value. The number of the printed bytes is updated in lyout::printed
+ * only in case the data are really written into the output.
+ */
+LY_ERR ly_write_skip(struct lyout *out, size_t len, size_t *position);
+
+/**
+ * @brief Write data into the hole at given position.
+ *
+ * @param[in] out Output specification.
+ * @param[in] position Position of the hole to fill, the value was provided by ly_write_skip().
+ * @param[in] buf Memory buffer with the data to print.
+ * @param[in] len Length of the data to print in the @p buf. Not that the length must correspond
+ * to the len value specified in the corresponding ly_write_skip() call.
+ * @return LY_ERR value. The number of the printed bytes is updated in lyout::printed
+ * only in case the data are really written into the output.
+ */
+LY_ERR ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t len);
+
+#endif /* LY_PRINTER_INTERNAL_H_ */
diff --git a/src/printer_schema.c b/src/printer_schema.c
new file mode 100644
index 0000000..c07ea79
--- /dev/null
+++ b/src/printer_schema.c
@@ -0,0 +1,193 @@
+/**
+ * @file printer_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic schema 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
+ */
+
+#include "common.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "printer_internal.h"
+#include "tree_schema.h"
+
+/**
+ * @brief Common schema printer.
+ *
+ * @param[in] out Prepared structure defining the type and details of the printer output.
+ * @param[in] module Schema to print.
+ * @param[in] format Output format.
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE printer.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in lyout::printed.
+ */
+static LY_ERR
+lys_print_(struct lyout *out, const struct lys_module *module, LYS_OUTFORMAT format, int UNUSED(line_length), int UNUSED(options))
+{
+    LY_ERR ret;
+
+    switch (format) {
+    case LYS_OUT_YANG:
+        ret = yang_print_parsed(out, module);
+        break;
+    case LYS_OUT_YANG_COMPILED:
+        ret = yang_print_compiled(out, module);
+        break;
+    /* TODO not yet implemented
+    case LYS_OUT_YIN:
+        lys_disable_deviations((struct lys_module *)module);
+        ret = yin_print_model(out, module);
+        lys_enable_deviations((struct lys_module *)module);
+        break;
+    case LYS_OUT_TREE:
+        ret = tree_print_model(out, module, target_node, line_length, options);
+        break;
+    case LYS_OUT_INFO:
+        ret = info_print_model(out, module, target_node);
+        break;
+    case LYS_OUT_JSON:
+        ret = jsons_print_model(out, module, target_node);
+        break;
+    */
+    default:
+        LOGERR(module->ctx, LY_EINVAL, "Unknown output format.");
+        ret = LY_EINVAL;
+        break;
+    }
+
+    return ret;
+}
+
+API ssize_t
+lys_print_file(FILE *f, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
+{
+    struct lyout out;
+    LY_ERR ret;
+
+    LY_CHECK_ARG_RET(NULL, f, module, LY_EINVAL);
+
+    memset(&out, 0, sizeof out);
+    out.ctx = module->ctx;
+    out.type = LYOUT_STREAM;
+    out.method.f = f;
+
+    ret = lys_print_(&out, module, format, line_length, options);
+    if (ret) {
+        /* error */
+        return (-1) * ret;
+    } else {
+        /* success */
+        return (ssize_t)out.printed;
+    }
+}
+
+API ssize_t
+lys_print_path(const char *path, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
+{
+    FILE *f;
+    ssize_t ret;
+
+    LY_CHECK_ARG_RET(NULL, path, module, LY_EINVAL);
+
+    f = fopen(path, "w");
+    if (!f) {
+        LOGERR(module->ctx, LY_ESYS, "Failed to open file \"%s\" (%s).", path, strerror(errno));
+        return LY_ESYS;
+    }
+
+    ret = lys_print_file(f, module, format, line_length, options);
+    fclose(f);
+    return ret;
+}
+
+API ssize_t
+lys_print_fd(int fd, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
+{
+    LY_ERR ret;
+    struct lyout out;
+
+    LY_CHECK_ARG_RET(NULL, fd >= 0, module, LY_EINVAL);
+
+    memset(&out, 0, sizeof out);
+    out.ctx = module->ctx;
+    out.type = LYOUT_FD;
+    out.method.fd = fd;
+
+    ret = lys_print_(&out, module, format, line_length, options);
+
+    if (out.type == LYOUT_FDSTREAM) {
+        /* close temporary stream based on the given file descriptor */
+        fclose(out.method.f);
+        /* move the original file descriptor to the end of the output file */
+        lseek(fd, 0, SEEK_END);
+    }
+
+    if (ret) {
+        /* error */
+        return (-1) * ret;
+    } else {
+        /* success */
+        return (ssize_t)out.printed;
+    }
+}
+
+API ssize_t
+lys_print_mem(char **strp, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
+{
+    struct lyout out;
+    LY_ERR ret;
+
+    LY_CHECK_ARG_RET(NULL, strp, module, LY_EINVAL);
+
+    memset(&out, 0, sizeof out);
+    out.ctx = module->ctx;
+    out.type = LYOUT_MEMORY;
+
+    ret = lys_print_(&out, module, format, line_length, options);
+    if (ret) {
+        /* error */
+        *strp = NULL;
+        return (-1) * ret;
+    } else {
+        /* success */
+        *strp = out.method.mem.buf;
+        return (ssize_t)out.printed;
+    }
+}
+
+API ssize_t
+lys_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lys_module *module,
+              LYS_OUTFORMAT format, int line_length, int options)
+{
+    LY_ERR ret;
+    struct lyout out;
+
+    LY_CHECK_ARG_RET(NULL, writeclb, module, LY_EINVAL);
+
+    memset(&out, 0, sizeof out);
+    out.ctx = module->ctx;
+    out.type = LYOUT_CALLBACK;
+    out.method.clb.f = writeclb;
+    out.method.clb.arg = arg;
+
+    ret = lys_print_(&out, module, format, line_length, options);
+    if (ret) {
+        /* error */
+        return (-1) * ret;
+    } else {
+        /* success */
+        return (ssize_t)out.printed;
+    }
+}
diff --git a/src/printer_schema.h b/src/printer_schema.h
new file mode 100644
index 0000000..613e768
--- /dev/null
+++ b/src/printer_schema.h
@@ -0,0 +1,91 @@
+/**
+ * @file printer_schema.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema printers for libyang
+ *
+ * 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
+ */
+
+#ifndef LY_PRINTER_SCHEMA_H_
+#define LY_PRINTER_SCHEMA_H_
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "tree_schema.h"
+
+/**
+ * @brief Print schema tree in the specified format into a memory block.
+ * It is up to caller to free the returned string by free().
+ *
+ * @param[out] strp Pointer to store the resulting dump.
+ * @param[in] module Schema tree to print.
+ * @param[in] format Schema output format.
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE printer.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lys_print_mem(char **strp, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options);
+
+/**
+ * @brief Print schema tree in the specified format into a file descriptor.
+ *
+ * @param[in] module Schema tree to print.
+ * @param[in] fd File descriptor where to print the data.
+ * @param[in] format Schema output format.
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE format.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lys_print_fd(int fd, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options);
+
+/**
+ * @brief Print schema tree in the specified format into a file stream.
+ *
+ * @param[in] module Schema tree to print.
+ * @param[in] f File stream where to print the schema.
+ * @param[in] format Schema output format.
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE printer.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lys_print_file(FILE *f, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options);
+
+/**
+ * @brief Print schema tree in the specified format into a file.
+ *
+ * @param[in] path File where to print the schema.
+ * @param[in] module Schema tree to print.
+ * @param[in] format Schema output format.
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE printer.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lys_print_path(const char *path, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options);
+
+/**
+ * @brief Print schema tree in the specified format using a provided callback.
+ *
+ * @param[in] module Schema tree to print.
+ * @param[in] writeclb Callback function to write the data (see write(1)).
+ * @param[in] arg Optional caller-specific argument to be passed to the \p writeclb callback.
+ * @param[in] format Schema output format.
+ * @param[in] line_length Maximum characters to be printed on a line, 0 for unlimited. Only for #LYS_OUT_TREE printer.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return Number of printed bytes in case of success.
+ * @return Negative value failure (absolute value corresponds to LY_ERR values).
+ */
+ssize_t lys_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg,
+                     const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options);
+
+#endif /* LY_PRINTER_SCHEMA_H_ */
diff --git a/src/printer_xml.c b/src/printer_xml.c
new file mode 100644
index 0000000..813d032
--- /dev/null
+++ b/src/printer_xml.c
@@ -0,0 +1,558 @@
+/**
+ * @file printer_xml.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief XML printer for libyang data structure
+ *
+ * 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
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "plugins_types.h"
+#include "printer_data.h"
+#include "printer_internal.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+#include "xml.h"
+
+/**
+ * @brief XML printer context.
+ */
+struct xmlpr_ctx {
+    struct lyout *out;  /**< output specification */
+    unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
+    int options;        /**< [Data printer flags](@ref dataprinterflags) */
+    int toplevel;       /**< top-level flag */
+};
+
+#define LEVEL ctx->level                     /**< current level */
+#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),""  /**< indentation parameters for printer functions */
+#define LEVEL_INC if (LEVEL) {LEVEL++;}      /**< increase indentation level */
+#define LEVEL_DEC if (LEVEL) {LEVEL--;}      /**< decrease indentation level */
+
+/**
+ * TODO
+ */
+struct mlist {
+    struct mlist *next;
+    struct lys_module *module;
+} *mlist = NULL, *mlist_new;
+
+#if 0
+static LY_ERR
+modlist_add(struct mlist **mlist, const struct lys_module *mod)
+{
+    struct mlist *iter;
+
+    for (iter = *mlist; iter; iter = iter->next) {
+        if (mod == iter->module) {
+            break;
+        }
+    }
+
+    if (!iter) {
+        iter = malloc(sizeof *iter);
+        LY_CHECK_ERR_RET(!iter, LOGMEM(mod->ctx), LY_EMEM);
+        iter->next = *mlist;
+        iter->module = (struct lys_module *)mod;
+        *mlist = iter;
+    }
+
+    return LY_SUCCESS;
+}
+#endif
+
+/**
+ * TODO
+ */
+static void
+xml_print_ns(struct xmlpr_ctx *ctx, const struct lyd_node *node)
+{
+    struct lyd_node *next, *cur, *child;
+    struct lyd_attr *attr;
+
+#if 0
+    struct mlist *mlist = NULL, *miter;
+
+    const struct lys_module *wdmod = NULL;
+
+    /* add node attribute modules */
+    for (attr = node->attr; attr; attr = attr->next) {
+        if (!strcmp(node->schema->name, "filter") &&
+                (!strcmp(node->schema->module->name, "ietf-netconf") ||
+                 !strcmp(node->schema->module->name, "notifications"))) {
+            /* exception for NETCONF's filter attributes */
+            continue;
+        } else {
+            r = modlist_add(&mlist, lys_main_module(attr->annotation->module));
+        }
+        if (r) {
+            goto print;
+        }
+    }
+#endif
+
+    /* add node children nodes and attribute modules */
+    switch (node->schema->nodetype) {
+    case LYS_LEAFLIST:
+    case LYS_LEAF:
+        /* TODO ietf-netconf-with-defaults namespace */
+#if 0
+        if (node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) {
+            /* get with-defaults module and print its namespace */
+            wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
+            if (wdmod && modlist_add(&mlist, wdmod)) {
+                goto print;
+            }
+        }
+#endif
+        break;
+    case LYS_CONTAINER:
+    case LYS_LIST:
+#if 0
+    case LYS_RPC:
+    case LYS_ACTION:
+    case LYS_NOTIF:
+        if (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG)) {
+            /* get with-defaults module and print its namespace */
+            wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
+            if (wdmod && modlist_add(&mlist, wdmod)) {
+                goto print;
+            }
+        }
+#endif
+        LY_LIST_FOR(((struct lyd_node_inner*)node)->child, child) {
+            LYD_TREE_DFS_BEGIN(child, next, cur) {
+                for (attr = cur->attr; attr; attr = attr->next) {
+                    if (!strcmp(cur->schema->name, "filter") &&
+                            (!strcmp(cur->schema->module->name, "ietf-netconf") ||
+                             !strcmp(cur->schema->module->name, "notifications"))) {
+                        /* exception for NETCONF's filter attributes */
+                        continue;
+                    } else {
+                        /* TODO annotations r = modlist_add(&mlist, lys_main_module(attr->annotation->module)); */
+                    }
+                }
+            LYD_TREE_DFS_END(child, next, cur)}
+        }
+        break;
+    default:
+        break;
+    }
+#if 0
+print:
+    /* print used namespaces */
+    while (mlist) {
+        miter = mlist;
+        mlist = mlist->next;
+
+        ly_print(ctx->out, " xmlns:%s=\"%s\"", miter->module->prefix, miter->module->ns);
+        free(miter);
+    }
+#endif
+}
+
+/**
+ * TODO
+ */
+static LY_ERR
+xml_print_attrs(struct xmlpr_ctx *ctx, const struct lyd_node *node)
+{
+    (void) ctx;
+    (void) node;
+
+#if 0
+    struct lyd_attr *attr;
+    const char **prefs, **nss;
+    const char *xml_expr = NULL, *mod_name;
+    uint32_t ns_count, i;
+    int rpc_filter = 0;
+    const struct lys_module *wdmod = NULL;
+    char *p;
+    size_t len;
+
+    LY_PRINT_SET;
+
+    /* with-defaults */
+    if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+        if ((node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) ||
+                (!node->dflt && (options & LYP_WD_ALL_TAG) && lyd_wd_default((struct lyd_node_leaf_list *)node))) {
+            /* we have implicit OR explicit default node */
+            /* get with-defaults module */
+            wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
+            if (wdmod) {
+                /* print attribute only if context include with-defaults schema */
+                ly_print(out, " %s:default=\"true\"", wdmod->prefix);
+            }
+        }
+    }
+    /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
+    if (!strcmp(node->schema->name, "filter")
+            && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
+        rpc_filter = 1;
+    }
+
+    for (attr = node->attr; attr; attr = attr->next) {
+        if (rpc_filter) {
+            /* exception for NETCONF's filter's attributes */
+            if (!strcmp(attr->name, "select")) {
+                /* xpath content, we have to convert the JSON format into XML first */
+                xml_expr = transform_json2xml(node->schema->module, attr->value_str, 0, &prefs, &nss, &ns_count);
+                if (!xml_expr) {
+                    /* error */
+                    return EXIT_FAILURE;
+                }
+
+                for (i = 0; i < ns_count; ++i) {
+                    ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
+                }
+                free(prefs);
+                free(nss);
+            }
+            ly_print(out, " %s=\"", attr->name);
+        } else {
+            ly_print(out, " %s:%s=\"", attr->annotation->module->prefix, attr->name);
+        }
+
+        switch (attr->value_type) {
+        case LY_TYPE_BINARY:
+        case LY_TYPE_STRING:
+        case LY_TYPE_BITS:
+        case LY_TYPE_ENUM:
+        case LY_TYPE_BOOL:
+        case LY_TYPE_DEC64:
+        case LY_TYPE_INT8:
+        case LY_TYPE_INT16:
+        case LY_TYPE_INT32:
+        case LY_TYPE_INT64:
+        case LY_TYPE_UINT8:
+        case LY_TYPE_UINT16:
+        case LY_TYPE_UINT32:
+        case LY_TYPE_UINT64:
+            if (attr->value_str) {
+                /* xml_expr can contain transformed xpath */
+                lyxml_dump_text(out, xml_expr ? xml_expr : attr->value_str, LYXML_DATA_ATTR);
+            }
+            break;
+
+        case LY_TYPE_IDENT:
+            if (!attr->value_str) {
+                break;
+            }
+            p = strchr(attr->value_str, ':');
+            assert(p);
+            len = p - attr->value_str;
+            mod_name = attr->annotation->module->name;
+            if (!strncmp(attr->value_str, mod_name, len) && !mod_name[len]) {
+                lyxml_dump_text(out, ++p, LYXML_DATA_ATTR);
+            } else {
+                /* avoid code duplication - use instance-identifier printer which gets necessary namespaces to print */
+                goto printinst;
+            }
+            break;
+        case LY_TYPE_INST:
+printinst:
+            xml_expr = transform_json2xml(node->schema->module, ((struct lyd_node_leaf_list *)node)->value_str, 1,
+                                          &prefs, &nss, &ns_count);
+            if (!xml_expr) {
+                /* error */
+                return EXIT_FAILURE;
+            }
+
+            for (i = 0; i < ns_count; ++i) {
+                ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
+            }
+            free(prefs);
+            free(nss);
+
+            lyxml_dump_text(out, xml_expr, LYXML_DATA_ATTR);
+            lydict_remove(node->schema->module->ctx, xml_expr);
+            break;
+
+        /* LY_TYPE_LEAFREF not allowed */
+        case LY_TYPE_EMPTY:
+            break;
+
+        default:
+            /* error */
+            LOGINT(node->schema->module->ctx);
+            return EXIT_FAILURE;
+        }
+
+        ly_print(out, "\"");
+
+        if (xml_expr) {
+            lydict_remove(node->schema->module->ctx, xml_expr);
+        }
+    }
+#endif
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print generic XML element despite of the data node type.
+ *
+ * Prints the element name, attributes and necessary namespaces.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] node Data node to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
+{
+    if (ctx->toplevel || !node->parent || node->schema->module != node->parent->schema->module) {
+        /* print "namespace" */
+        ly_print(ctx->out, "%*s<%s xmlns=\"%s\"", INDENT, node->schema->name, node->schema->module->ns);
+    } else {
+        ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
+    }
+
+    if (ctx->toplevel) {
+        xml_print_ns(ctx, node);
+        ctx->toplevel = 0;
+    }
+
+    LY_CHECK_RET(xml_print_attrs(ctx, node));
+
+    return LY_SUCCESS;
+}
+
+static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
+
+/**
+ * @brief Print XML element representing lyd_node_term.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] node Data node to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
+{
+    LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
+
+    if (((struct lysc_node_leaf*)node->schema)->type->plugin->flags & LY_TYPE_FLAG_PREFIXES) {
+        /* TODO get prefixes from the value and print namespaces */
+    }
+
+    if (!node->value.canonized || !node->value.canonized[0]) {
+        ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
+    } else {
+        ly_print(ctx->out, ">");
+        lyxml_dump_text(ctx->out, node->value.canonized, 0);
+        ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print XML element representing lyd_node_inner.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] node Data node to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
+{
+    LY_ERR ret;
+    struct lyd_node *child;
+
+    LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
+
+    if (!node->child) {
+        ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
+        return LY_SUCCESS;
+    }
+
+    /* children */
+    ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
+
+    LEVEL_INC;
+    LY_LIST_FOR(node->child, child) {
+        ret = xml_print_node(ctx, child);
+        LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
+    }
+    LEVEL_DEC;
+
+    ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
+
+    return LY_SUCCESS;
+}
+
+#if 0
+static int
+xml_print_anydata(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
+{
+    char *buf;
+    struct lyd_node_anydata *any = (struct lyd_node_anydata *)node;
+    struct lyd_node *iter;
+    const char *ns;
+
+    LY_PRINT_SET;
+
+    if (toplevel || !node->parent || nscmp(node, node->parent)) {
+        /* print "namespace" */
+        ns = lyd_node_module(node)->ns;
+        ly_print(out, "%*s<%s xmlns=\"%s\"", INDENT, node->schema->name, ns);
+    } else {
+        ly_print(out, "%*s<%s", INDENT, node->schema->name);
+    }
+
+    if (toplevel) {
+        xml_print_ns(out, node, options);
+    }
+    if (xml_print_attrs(out, node, options)) {
+        return EXIT_FAILURE;
+    }
+    if (!(void*)any->value.tree || (any->value_type == LYD_ANYDATA_CONSTSTRING && !any->value.str[0])) {
+        /* no content */
+        ly_print(out, "/>%s", level ? "\n" : "");
+    } else {
+        if (any->value_type == LYD_ANYDATA_DATATREE) {
+            /* print namespaces in the anydata data tree */
+            LY_TREE_FOR(any->value.tree, iter) {
+                xml_print_ns(out, iter, options);
+            }
+        }
+        /* close opening tag ... */
+        ly_print(out, ">");
+        /* ... and print anydata content */
+        switch (any->value_type) {
+        case LYD_ANYDATA_CONSTSTRING:
+            lyxml_dump_text(out, any->value.str, LYXML_DATA_ELEM);
+            break;
+        case LYD_ANYDATA_DATATREE:
+            if (any->value.tree) {
+                if (level) {
+                    ly_print(out, "\n");
+                }
+                LY_TREE_FOR(any->value.tree, iter) {
+                    if (xml_print_node(out, level ? level + 1 : 0, iter, 0, (options & ~(LYP_WITHSIBLINGS | LYP_NETCONF)))) {
+                        return EXIT_FAILURE;
+                    }
+                }
+            }
+            break;
+        case LYD_ANYDATA_XML:
+            lyxml_print_mem(&buf, any->value.xml, (level ? LYXML_PRINT_FORMAT | LYXML_PRINT_NO_LAST_NEWLINE : 0)
+                                                   | LYXML_PRINT_SIBLINGS);
+            ly_print(out, "%s%s", level ? "\n" : "", buf);
+            free(buf);
+            break;
+        case LYD_ANYDATA_SXML:
+            /* print without escaping special characters */
+            ly_print(out, "%s", any->value.str);
+            break;
+        case LYD_ANYDATA_JSON:
+        case LYD_ANYDATA_LYB:
+            /* JSON and LYB format is not supported */
+            LOGWRN(node->schema->module->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
+            break;
+        case LYD_ANYDATA_STRING:
+        case LYD_ANYDATA_SXMLD:
+        case LYD_ANYDATA_JSOND:
+        case LYD_ANYDATA_LYBD:
+            /* dynamic strings are used only as input parameters */
+            assert(0);
+            break;
+        }
+
+        /* closing tag */
+        ly_print(out, "</%s>%s", node->schema->name, level ? "\n" : "");
+    }
+
+    LY_PRINT_RET(node->schema->module->ctx);
+}
+#endif
+
+/**
+ * @brief Print XML element representing lyd_node.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] node Data node to be printed.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
+{
+    LY_ERR ret = LY_SUCCESS;
+
+#if 0
+    if (!lyd_wd_toprint(node, ctx->options)) {
+        /* wd says do not print */
+        return EXIT_SUCCESS;
+    }
+#endif
+
+    switch (node->schema->nodetype) {
+#if 0
+    case LYS_NOTIF:
+    case LYS_ACTION:
+#endif
+    case LYS_CONTAINER:
+    case LYS_LIST:
+        ret = xml_print_inner(ctx, (const struct lyd_node_inner*)node);
+        break;
+    case LYS_LEAF:
+    case LYS_LEAFLIST:
+        ret = xml_print_term(ctx, (const struct lyd_node_term*)node);
+        break;
+#if 0
+    case LYS_ANYXML:
+    case LYS_ANYDATA:
+        ret = xml_print_anydata(ctx, node);
+        break;
+#endif
+    default:
+        LOGINT(node->schema->module->ctx);
+        ret = LY_EINT;
+        break;
+    }
+
+    return ret;
+}
+
+LY_ERR
+xml_print_data(struct lyout *out, const struct lyd_node *root, int options)
+{
+    const struct lyd_node *node;
+    struct xmlpr_ctx ctx_ = {.out = out, .level = (options & LYDP_FORMAT ? 1 : 0), .options = options, .toplevel = 1}, *ctx = &ctx_;
+
+    if (!root) {
+        if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
+            ly_print(out, "");
+        }
+        goto finish;
+    }
+
+    /* content */
+    LY_LIST_FOR(root, node) {
+        if (xml_print_node(ctx, node)) {
+            return EXIT_FAILURE;
+        }
+        if (!(options & LYDP_WITHSIBLINGS)) {
+            break;
+        }
+    }
+
+finish:
+    ly_print_flush(out);
+    return LY_SUCCESS;
+}
+
diff --git a/src/printer_yang.c b/src/printer_yang.c
new file mode 100755
index 0000000..c429094
--- /dev/null
+++ b/src/printer_yang.c
@@ -0,0 +1,2319 @@
+/**
+ * @file printer_yang.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief YANG printer
+ *
+ * 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
+ */
+
+#include "common.h"
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extensions.h"
+#include "log.h"
+#include "printer_internal.h"
+#include "tree.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+/**
+ * @brief Types of the YANG printers
+ */
+enum schema_type {
+    YPR_PARSED,   /**< YANG printer of the parsed schema */
+    YPR_COMPILED  /**< YANG printer of the compiled schema */
+};
+
+/**
+ * @brief YANG printer context.
+ */
+struct ypr_ctx {
+    struct lyout *out;               /**< output specification */
+    unsigned int level;              /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
+    const struct lys_module *module; /**< schema to print */
+    enum schema_type schema;         /**< type of the schema to print */
+};
+
+#define LEVEL ctx->level             /**< current level */
+#define INDENT (LEVEL)*2,""          /**< indentation parameters for printer functions */
+
+/**
+ * @brief Print the given text as content of a double quoted YANG string,
+ * including encoding characters that have special meanings. The quotation marks
+ * are not printed.
+ *
+ * Follows RFC 7950, section 6.1.3.
+ *
+ * @param[in] out Output specification.
+ * @param[in] text String to be printed.
+ * @param[in] len Length of the string from @p text to be printed. In case of 0,
+ * the @p text is printed completely as a NULL-terminated string.
+ */
+static void
+ypr_encode(struct lyout *out, const char *text, int len)
+{
+    int i, start_len;
+    const char *start;
+    char special = 0;
+
+    if (!len) {
+        return;
+    }
+
+    if (len < 0) {
+        len = strlen(text);
+    }
+
+    start = text;
+    start_len = 0;
+    for (i = 0; i < len; ++i) {
+        switch (text[i]) {
+        case '\n':
+        case '\t':
+        case '\"':
+        case '\\':
+            special = text[i];
+            break;
+        default:
+            ++start_len;
+            break;
+        }
+
+        if (special) {
+            ly_write(out, start, start_len);
+            switch (special) {
+            case '\n':
+                ly_write(out, "\\n", 2);
+                break;
+            case '\t':
+                ly_write(out, "\\t", 2);
+                break;
+            case '\"':
+                ly_write(out, "\\\"", 2);
+                break;
+            case '\\':
+                ly_write(out, "\\\\", 2);
+                break;
+            }
+
+            start += start_len + 1;
+            start_len = 0;
+
+            special = 0;
+        }
+    }
+
+    ly_write(out, start, start_len);
+}
+
+static void
+ypr_open(struct lyout *out, int *flag)
+{
+    if (flag && !*flag) {
+        *flag = 1;
+        ly_print(out, " {\n");
+    }
+}
+
+static void
+ypr_close(struct ypr_ctx *ctx, int flag)
+{
+    if (flag) {
+        ly_print(ctx->out, "%*s}\n", INDENT);
+    } else {
+        ly_print(ctx->out, ";\n");
+    }
+}
+
+static void
+ypr_text(struct ypr_ctx *ctx, const char *name, const char *text, int singleline, int closed)
+{
+    const char *s, *t;
+
+    if (singleline) {
+        ly_print(ctx->out, "%*s%s \"", INDENT, name);
+    } else {
+        ly_print(ctx->out, "%*s%s\n", INDENT, name);
+        LEVEL++;
+
+        ly_print(ctx->out, "%*s\"", INDENT);
+    }
+    t = text;
+    while ((s = strchr(t, '\n'))) {
+        ypr_encode(ctx->out, t, s - t);
+        ly_print(ctx->out, "\n");
+        t = s + 1;
+        if (*t != '\n') {
+            ly_print(ctx->out, "%*s ", INDENT);
+        }
+    }
+
+    ypr_encode(ctx->out, t, strlen(t));
+    if (closed) {
+        ly_print(ctx->out, "\";\n");
+    } else {
+        ly_print(ctx->out, "\"");
+    }
+    if (!singleline) {
+        LEVEL--;
+    }
+}
+
+static void
+yprp_stmt(struct ypr_ctx *ctx, struct lysp_stmt *stmt)
+{
+    struct lysp_stmt *childstmt;
+    const char *s, *t;
+
+    if (stmt->arg) {
+        if (stmt->flags) {
+            ly_print(ctx->out, "%*s%s\n", INDENT, stmt->stmt);
+            LEVEL++;
+            ly_print(ctx->out, "%*s%c", INDENT, (stmt->flags & LYS_DOUBLEQUOTED) ? '\"' : '\'');
+            t = stmt->arg;
+            while ((s = strchr(t, '\n'))) {
+                ypr_encode(ctx->out, t, s - t);
+                ly_print(ctx->out, "\n");
+                t = s + 1;
+                if (*t != '\n') {
+                    ly_print(ctx->out, "%*s ", INDENT);
+                }
+            }
+            LEVEL--;
+            ypr_encode(ctx->out, t, strlen(t));
+            ly_print(ctx->out, "%c%s", (stmt->flags & LYS_DOUBLEQUOTED) ? '\"' : '\'', stmt->child ? " {\n" : ";\n");
+        } else {
+            ly_print(ctx->out, "%*s%s %s%s", INDENT, stmt->stmt, stmt->arg, stmt->child ? " {\n" : ";\n");
+        }
+    } else {
+        ly_print(ctx->out, "%*s%s%s", INDENT, stmt->stmt, stmt->child ? " {\n" : ";\n");
+    }
+
+    if (stmt->child) {
+        LEVEL++;
+        LY_LIST_FOR(stmt->child, childstmt) {
+            yprp_stmt(ctx, childstmt);
+        }
+        LEVEL--;
+        ly_print(ctx->out, "%*s}\n", INDENT);
+    }
+}
+
+/**
+ * @param[in] count Number of extensions to print, 0 to print them all.
+ */
+static void
+yprp_extension_instances(struct ypr_ctx *ctx, LYEXT_SUBSTMT substmt, uint8_t substmt_index,
+                               struct lysp_ext_instance *ext, int *flag, unsigned int count)
+{
+    unsigned int u;
+    struct lysp_stmt *stmt;
+
+    if (!count && ext) {
+        count = LY_ARRAY_SIZE(ext);
+    }
+    LY_ARRAY_FOR(ext, u) {
+        if (!count) {
+            break;
+        }
+        if (ext->insubstmt == substmt && ext->insubstmt_index == substmt_index) {
+            ypr_open(ctx->out, flag);
+            if (ext[u].argument) {
+                ly_print(ctx->out, "%*s%s %s%s", INDENT, ext[u].name, ext[u].argument, ext[u].child ? " {\n" : ";\n");
+            } else {
+                ly_print(ctx->out, "%*s%s%s", INDENT, ext[u].name, ext[u].child ? " {\n" : ";\n");
+            }
+
+            if (ext[u].child) {
+                LEVEL++;
+                LY_LIST_FOR(ext[u].child, stmt) {
+                    yprp_stmt(ctx, stmt);
+                }
+                LEVEL--;
+                ly_print(ctx->out, "%*s}\n", INDENT);
+            }
+        }
+        count--;
+    }
+}
+
+/**
+ * @param[in] count Number of extensions to print, 0 to print them all.
+ */
+static void
+yprc_extension_instances(struct ypr_ctx *ctx, LYEXT_SUBSTMT substmt, uint8_t substmt_index,
+                                 struct lysc_ext_instance *ext, int *flag, unsigned int count)
+{
+    unsigned int u;
+
+    if (!count && ext) {
+        count = LY_ARRAY_SIZE(ext);
+    }
+    LY_ARRAY_FOR(ext, u) {
+        if (!count) {
+            break;
+        }
+        /* TODO compiled extensions */
+        (void) ctx;
+        (void) substmt;
+        (void) substmt_index;
+        (void) flag;
+
+        count--;
+    }
+}
+
+static void
+ypr_substmt(struct ypr_ctx *ctx, LYEXT_SUBSTMT substmt, uint8_t substmt_index, const char *text, void *ext)
+{
+    unsigned int u;
+    int extflag = 0;
+
+    if (!text) {
+        /* nothing to print */
+        return;
+    }
+
+    if (ext_substmt_info[substmt].flags & SUBST_FLAG_ID) {
+        ly_print(ctx->out, "%*s%s %s", INDENT, ext_substmt_info[substmt].name, text);
+    } else {
+        ypr_text(ctx, ext_substmt_info[substmt].name, text,
+                 (ext_substmt_info[substmt].flags & SUBST_FLAG_YIN) ? 0 : 1, 0);
+    }
+
+    LEVEL++;
+    LY_ARRAY_FOR(ext, u) {
+        if (((struct lysp_ext_instance*)ext)[u].insubstmt != substmt || ((struct lysp_ext_instance*)ext)[u].insubstmt_index != substmt_index) {
+            continue;
+        }
+        if (ctx->schema == YPR_PARSED) {
+            yprp_extension_instances(ctx, substmt, substmt_index, &((struct lysp_ext_instance*)ext)[u], &extflag, 1);
+        } else {
+            yprc_extension_instances(ctx, substmt, substmt_index, &((struct lysc_ext_instance*)ext)[u], &extflag, 1);
+        }
+    }
+    LEVEL--;
+    ypr_close(ctx, extflag);
+}
+
+static void
+ypr_unsigned(struct ypr_ctx *ctx, LYEXT_SUBSTMT substmt, uint8_t substmt_index, void *exts, unsigned int attr_value, int *flag)
+{
+    char *str;
+
+    if (asprintf(&str, "%u", attr_value) == -1) {
+        LOGMEM(ctx->module->ctx);
+        ctx->out->status = LY_EMEM;
+        return;
+    }
+    ypr_open(ctx->out, flag);
+    ypr_substmt(ctx, substmt, substmt_index, str, exts);
+    free(str);
+}
+
+static void
+ypr_signed(struct ypr_ctx *ctx, LYEXT_SUBSTMT substmt, uint8_t substmt_index, void *exts, signed int attr_value, int *flag)
+{
+    char *str;
+
+    if (asprintf(&str, "%d", attr_value) == -1) {
+        LOGMEM(ctx->module->ctx);
+        ctx->out->status = LY_EMEM;
+        return;
+    }
+    ypr_open(ctx->out, flag);
+    ypr_substmt(ctx, substmt, substmt_index, str, exts);
+    free(str);
+}
+
+static void
+yprp_revision(struct ypr_ctx *ctx, const struct lysp_revision *rev)
+{
+    if (rev->dsc || rev->ref || rev->exts) {
+        ly_print(ctx->out, "%*srevision %s {\n", INDENT, rev->date);
+        LEVEL++;
+        yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, rev->exts, NULL, 0);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DESCRIPTION, 0, rev->dsc, rev->exts);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_REFERENCE, 0, rev->ref, rev->exts);
+        LEVEL--;
+        ly_print(ctx->out, "%*s}\n", INDENT);
+    } else {
+        ly_print(ctx->out, "%*srevision %s;\n", INDENT, rev->date);
+    }
+}
+
+static void
+ypr_mandatory(struct ypr_ctx *ctx, uint16_t flags, void *exts, int *flag)
+{
+    if (flags & LYS_MAND_MASK) {
+        ypr_open(ctx->out, flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_MANDATORY, 0, (flags & LYS_MAND_TRUE) ? "true" : "false", exts);
+    }
+}
+
+static void
+ypr_config(struct ypr_ctx *ctx, uint16_t flags, void *exts, int *flag)
+{
+    if (flags & LYS_CONFIG_MASK) {
+        ypr_open(ctx->out, flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_CONFIG, 0, (flags & LYS_CONFIG_W) ? "true" : "false", exts);
+    }
+}
+
+static void
+ypr_status(struct ypr_ctx *ctx, uint16_t flags, void *exts, int *flag)
+{
+    const char *status = NULL;
+
+    if (flags & LYS_STATUS_CURR) {
+        ypr_open(ctx->out, flag);
+        status = "current";
+    } else if (flags & LYS_STATUS_DEPRC) {
+        ypr_open(ctx->out, flag);
+        status = "deprecated";
+    } else if (flags & LYS_STATUS_OBSLT) {
+        ypr_open(ctx->out, flag);
+        status = "obsolete";
+    }
+
+    ypr_substmt(ctx, LYEXT_SUBSTMT_STATUS, 0, status, exts);
+}
+
+static void
+ypr_description(struct ypr_ctx *ctx, const char *dsc, void *exts, int *flag)
+{
+    if (dsc) {
+        ypr_open(ctx->out, flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DESCRIPTION, 0, dsc, exts);
+    }
+}
+
+static void
+ypr_reference(struct ypr_ctx *ctx, const char *ref, void *exts, int *flag)
+{
+    if (ref) {
+        ypr_open(ctx->out, flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_REFERENCE, 0, ref, exts);
+    }
+}
+
+static void
+yprp_iffeatures(struct ypr_ctx *ctx, const char **iff, struct lysp_ext_instance *exts, int *flag)
+{
+    unsigned int u;
+    int extflag;
+
+    LY_ARRAY_FOR(iff, u) {
+        ypr_open(ctx->out, flag);
+        extflag = 0;
+
+        ly_print(ctx->out, "%*sif-feature \"%s\"", INDENT, iff[u]);
+
+        /* extensions */
+        LEVEL++;
+        LY_ARRAY_FOR(exts, u) {
+            if (exts[u].insubstmt != LYEXT_SUBSTMT_IFFEATURE || exts[u].insubstmt_index != u) {
+                continue;
+            }
+            yprp_extension_instances(ctx, LYEXT_SUBSTMT_IFFEATURE, u, &exts[u], &extflag, 1);
+        }
+        LEVEL--;
+        ypr_close(ctx, extflag);
+    }
+}
+
+static void
+yprc_iffeature(struct ypr_ctx *ctx, struct lysc_iffeature *feat, int *index_e, int *index_f)
+{
+    int brackets_flag = *index_e;
+    uint8_t op;
+
+    op = lysc_iff_getop(feat->expr, *index_e);
+    (*index_e)++;
+
+    switch (op) {
+    case LYS_IFF_F:
+        if (ctx->module == feat->features[*index_f]->module) {
+            ly_print(ctx->out, "%s", feat->features[*index_f]->name);
+        } else {
+            ly_print(ctx->out, "%s:%s", feat->features[*index_f]->module->prefix, feat->features[*index_f]->name);
+        }
+        (*index_f)++;
+        break;
+    case LYS_IFF_NOT:
+        ly_print(ctx->out, "not ");
+        yprc_iffeature(ctx, feat, index_e, index_f);
+        break;
+    case LYS_IFF_AND:
+        if (brackets_flag) {
+            /* AND need brackets only if previous op was not */
+            if (*index_e < 2 || lysc_iff_getop(feat->expr, *index_e - 2) != LYS_IFF_NOT) {
+                brackets_flag = 0;
+            }
+        }
+        /* falls through */
+    case LYS_IFF_OR:
+        if (brackets_flag) {
+            ly_print(ctx->out, "(");
+        }
+        yprc_iffeature(ctx, feat, index_e, index_f);
+        ly_print(ctx->out, " %s ", op == LYS_IFF_OR ? "or" : "and");
+        yprc_iffeature(ctx, feat, index_e, index_f);
+        if (brackets_flag) {
+            ly_print(ctx->out, ")");
+        }
+    }
+}
+
+static void
+yprc_iffeatures(struct ypr_ctx *ctx, struct lysc_iffeature *iff, struct lysc_ext_instance *exts, int *flag)
+{
+    unsigned int u;
+    int extflag;
+
+    LY_ARRAY_FOR(iff, u) {
+        int index_e = 0, index_f = 0;
+
+        ypr_open(ctx->out, flag);
+        extflag = 0;
+
+        ly_print(ctx->out, "%*sif-feature \"", INDENT);
+        yprc_iffeature(ctx, iff, &index_e, &index_f);
+        ly_print(ctx->out, "\"");
+
+        /* extensions */
+        LEVEL++;
+        LY_ARRAY_FOR(exts, u) {
+            if (exts[u].insubstmt != LYEXT_SUBSTMT_IFFEATURE || exts[u].insubstmt_index != u) {
+                continue;
+            }
+            yprc_extension_instances(ctx, LYEXT_SUBSTMT_IFFEATURE, u, &exts[u], &extflag, 1);
+        }
+        LEVEL--;
+        ypr_close(ctx, extflag);
+    }
+}
+
+static void
+yprp_extension(struct ypr_ctx *ctx, const struct lysp_ext *ext)
+{
+    int flag = 0, flag2 = 0;
+    unsigned int i;
+
+    ly_print(ctx->out, "%*sextension %s", INDENT, ext->name);
+    LEVEL++;
+
+    if (ext->exts) {
+        yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, ext->exts, &flag, 0);
+    }
+
+    if (ext->argument) {
+        ypr_open(ctx->out, &flag);
+        ly_print(ctx->out, "%*sargument %s", INDENT, ext->argument);
+        if (ext->exts) {
+            LEVEL++;
+            i = -1;
+            while ((i = lysp_ext_instance_iter(ext->exts, i + 1, LYEXT_SUBSTMT_ARGUMENT)) != LY_ARRAY_SIZE(ext->exts)) {
+                yprp_extension_instances(ctx, LYEXT_SUBSTMT_ARGUMENT, 0, &ext->exts[i], &flag2, 1);
+            }
+            LEVEL--;
+        }
+        if ((ext->flags & LYS_YINELEM_MASK) ||
+                (ext->exts && lysp_ext_instance_iter(ext->exts, 0, LYEXT_SUBSTMT_YINELEM) != LY_ARRAY_SIZE(ext->exts))) {
+            ypr_open(ctx->out, &flag2);
+            ypr_substmt(ctx, LYEXT_SUBSTMT_YINELEM, 0, (ext->flags & LYS_YINELEM_TRUE) ? "true" : "false", ext->exts);
+        }
+        ypr_close(ctx, flag2);
+    }
+
+    ypr_status(ctx, ext->flags, ext->exts, &flag);
+    ypr_description(ctx, ext->dsc, ext->exts, &flag);
+    ypr_reference(ctx, ext->ref, ext->exts, &flag);
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_feature(struct ypr_ctx *ctx, const struct lysp_feature *feat)
+{
+    int flag = 0;
+
+    ly_print(ctx->out, "\n%*sfeature %s", INDENT, feat->name);
+    LEVEL++;
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, feat->exts, &flag, 0);
+    yprp_iffeatures(ctx, feat->iffeatures, feat->exts, &flag);
+    ypr_status(ctx, feat->flags, feat->exts, &flag);
+    ypr_description(ctx, feat->dsc, feat->exts, &flag);
+    ypr_reference(ctx, feat->ref, feat->exts, &flag);
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_feature(struct ypr_ctx *ctx, const struct lysc_feature *feat)
+{
+    int flag = 0;
+
+    ly_print(ctx->out, "\n%*sfeature %s", INDENT, feat->name);
+    LEVEL++;
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, feat->exts, &flag, 0);
+    yprc_iffeatures(ctx, feat->iffeatures, feat->exts, &flag);
+    ypr_status(ctx, feat->flags, feat->exts, &flag);
+    ypr_description(ctx, feat->dsc, feat->exts, &flag);
+    ypr_reference(ctx, feat->ref, feat->exts, &flag);
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_identity(struct ypr_ctx *ctx, const struct lysp_ident *ident)
+{
+    int flag = 0;
+    unsigned int u;
+
+    ly_print(ctx->out, "\n%*sidentity %s", INDENT, ident->name);
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, ident->exts, &flag, 0);
+    yprp_iffeatures(ctx, ident->iffeatures, ident->exts, &flag);
+
+    LY_ARRAY_FOR(ident->bases, u) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_BASE, u, ident->bases[u], ident->exts);
+    }
+
+    ypr_status(ctx, ident->flags, ident->exts, &flag);
+    ypr_description(ctx, ident->dsc, ident->exts, &flag);
+    ypr_reference(ctx, ident->ref, ident->exts, &flag);
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_identity(struct ypr_ctx *ctx, const struct lysc_ident *ident)
+{
+    int flag = 0;
+    unsigned int u;
+
+    ly_print(ctx->out, "\n%*sidentity %s", INDENT, ident->name);
+    LEVEL++;
+
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, ident->exts, &flag, 0);
+    yprc_iffeatures(ctx, ident->iffeatures, ident->exts, &flag);
+
+    LY_ARRAY_FOR(ident->derived, u) {
+        ypr_open(ctx->out, &flag);
+        if (ctx->module != ident->derived[u]->module) {
+            ly_print(ctx->out, "%*sderived %s:%s;\n", INDENT, ident->derived[u]->module->prefix, ident->derived[u]->name);
+        } else {
+            ly_print(ctx->out, "%*sderived %s;\n", INDENT, ident->derived[u]->name);
+        }
+    }
+
+    ypr_status(ctx, ident->flags, ident->exts, &flag);
+    ypr_description(ctx, ident->dsc, ident->exts, &flag);
+    ypr_reference(ctx, ident->ref, ident->exts, &flag);
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_restr(struct ypr_ctx *ctx, const struct lysp_restr *restr, const char *name, int *flag)
+{
+    int inner_flag = 0;
+
+    if (!restr) {
+        return;
+    }
+
+    ypr_open(ctx->out, flag);
+    ly_print(ctx->out, "%*s%s \"", INDENT, name);
+    ypr_encode(ctx->out, (restr->arg[0] != 0x15 && restr->arg[0] != 0x06) ? restr->arg : &restr->arg[1], -1);
+    ly_print(ctx->out, "\"");
+
+    LEVEL++;
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, restr->exts, &inner_flag, 0);
+    if (restr->arg[0] == 0x15) {
+        /* special byte value in pattern's expression: 0x15 - invert-match, 0x06 - match */
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_MODIFIER, 0, "invert-match", restr->exts);
+    }
+    if (restr->emsg) {
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ERRMSG, 0, restr->emsg, restr->exts);
+    }
+    if (restr->eapptag) {
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ERRTAG, 0, restr->eapptag, restr->exts);
+    }
+    ypr_description(ctx, restr->dsc, restr->exts, &inner_flag);
+    ypr_reference(ctx, restr->ref, restr->exts, &inner_flag);
+
+    LEVEL--;
+    ypr_close(ctx, inner_flag);
+}
+
+static void
+yprc_must(struct ypr_ctx *ctx, const struct lysc_must *must, int *flag)
+{
+    int inner_flag = 0;
+
+    ypr_open(ctx->out, flag);
+    ly_print(ctx->out, "%*smust \"", INDENT);
+    ypr_encode(ctx->out, must->cond->expr, -1);
+    ly_print(ctx->out, "\"");
+
+    LEVEL++;
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, must->exts, &inner_flag, 0);
+    if (must->emsg) {
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ERRMSG, 0, must->emsg, must->exts);
+    }
+    if (must->eapptag) {
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ERRTAG, 0, must->eapptag, must->exts);
+    }
+    ypr_description(ctx, must->dsc, must->exts, &inner_flag);
+    ypr_reference(ctx, must->ref, must->exts, &inner_flag);
+
+    LEVEL--;
+    ypr_close(ctx, inner_flag);
+}
+
+static void
+yprc_range(struct ypr_ctx *ctx, const struct lysc_range *range, LY_DATA_TYPE basetype, int *flag)
+{
+    int inner_flag = 0;
+    unsigned int u;
+
+    ypr_open(ctx->out, flag);
+    ly_print(ctx->out, "%*s%s \"", (basetype == LY_TYPE_STRING || basetype == LY_TYPE_BINARY)? "length" : "range", INDENT);
+    LY_ARRAY_FOR(range->parts, u) {
+        if (u > 0) {
+            ly_print(ctx->out, " | ");
+        }
+        if (range->parts[u].max_64 == range->parts[u].min_64) {
+            if (basetype <= LY_TYPE_STRING) { /* unsigned values */
+                ly_print(ctx->out, "%"PRIu64, range->parts[u].max_u64);
+            } else { /* signed values */
+                ly_print(ctx->out, "%"PRId64, range->parts[u].max_64);
+            }
+        } else {
+            if (basetype <= LY_TYPE_STRING) { /* unsigned values */
+                ly_print(ctx->out, "%"PRIu64"..%"PRIu64, range->parts[u].min_u64, range->parts[u].max_u64);
+            } else { /* signed values */
+                ly_print(ctx->out, "%"PRId64"..%"PRId64, range->parts[u].min_64, range->parts[u].max_64);
+            }
+        }
+    }
+    ly_print(ctx->out, "\"");
+
+    LEVEL++;
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, range->exts, &inner_flag, 0);
+    if (range->emsg) {
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ERRMSG, 0, range->emsg, range->exts);
+    }
+    if (range->eapptag) {
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ERRTAG, 0, range->eapptag, range->exts);
+    }
+    ypr_description(ctx, range->dsc, range->exts, &inner_flag);
+    ypr_reference(ctx, range->ref, range->exts, &inner_flag);
+
+    LEVEL--;
+    ypr_close(ctx, inner_flag);
+}
+
+static void
+yprc_pattern(struct ypr_ctx *ctx, const struct lysc_pattern *pattern, int *flag)
+{
+    int inner_flag = 0;
+
+    ypr_open(ctx->out, flag);
+    ly_print(ctx->out, "%*spattern \"", INDENT);
+    ypr_encode(ctx->out, pattern->expr, -1);
+    ly_print(ctx->out, "\"");
+
+    LEVEL++;
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, pattern->exts, &inner_flag, 0);
+    if (pattern->inverted) {
+        /* special byte value in pattern's expression: 0x15 - invert-match, 0x06 - match */
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_MODIFIER, 0, "invert-match", pattern->exts);
+    }
+    if (pattern->emsg) {
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ERRMSG, 0, pattern->emsg, pattern->exts);
+    }
+    if (pattern->eapptag) {
+        ypr_open(ctx->out, &inner_flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ERRTAG, 0, pattern->eapptag, pattern->exts);
+    }
+    ypr_description(ctx, pattern->dsc, pattern->exts, &inner_flag);
+    ypr_reference(ctx, pattern->ref, pattern->exts, &inner_flag);
+
+    LEVEL--;
+    ypr_close(ctx, inner_flag);
+}
+
+static void
+yprp_when(struct ypr_ctx *ctx, struct lysp_when *when, int *flag)
+{
+    int inner_flag = 0;
+
+    if (!when) {
+        return;
+    }
+    ypr_open(ctx->out, flag);
+
+    ly_print(ctx->out, "%*swhen \"", INDENT);
+    ypr_encode(ctx->out, when->cond, -1);
+    ly_print(ctx->out, "\"");
+
+    LEVEL++;
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, when->exts, &inner_flag, 0);
+    ypr_description(ctx, when->dsc, when->exts, &inner_flag);
+    ypr_reference(ctx, when->ref, when->exts, &inner_flag);
+    LEVEL--;
+    ypr_close(ctx, inner_flag);
+}
+
+static void
+yprc_when(struct ypr_ctx *ctx, struct lysc_when *when, int *flag)
+{
+    int inner_flag = 0;
+
+    if (!when) {
+        return;
+    }
+    ypr_open(ctx->out, flag);
+
+    ly_print(ctx->out, "%*swhen \"", INDENT);
+    ypr_encode(ctx->out, when->cond->expr, -1);
+    ly_print(ctx->out, "\"");
+
+    LEVEL++;
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, when->exts, &inner_flag, 0);
+    ypr_description(ctx, when->dsc, when->exts, &inner_flag);
+    ypr_reference(ctx, when->ref, when->exts, &inner_flag);
+    LEVEL--;
+    ypr_close(ctx, inner_flag);
+}
+
+static void
+yprp_enum(struct ypr_ctx *ctx, const struct lysp_type_enum *items, LY_DATA_TYPE type, int *flag)
+{
+    unsigned int u;
+    int inner_flag;
+
+    LY_ARRAY_FOR(items, u) {
+        ypr_open(ctx->out, flag);
+        ly_print(ctx->out, "%*s%s \"", INDENT, type == LY_TYPE_BITS ? "bit" : "enum");
+        ypr_encode(ctx->out, items[u].name, -1);
+        ly_print(ctx->out, "\"");
+        inner_flag = 0;
+        LEVEL++;
+        yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, items[u].exts, &inner_flag, 0);
+        yprp_iffeatures(ctx, items[u].iffeatures, items[u].exts, &inner_flag);
+        if (items[u].flags & LYS_SET_VALUE) {
+            if (type == LY_TYPE_BITS) {
+                ypr_unsigned(ctx, LYEXT_SUBSTMT_POSITION, 0, items[u].exts, items[u].value, &inner_flag);
+            } else { /* LY_TYPE_ENUM */
+                ypr_signed(ctx, LYEXT_SUBSTMT_VALUE, 0, items[u].exts, items[u].value, &inner_flag);
+            }
+        }
+        ypr_status(ctx, items[u].flags, items[u].exts, &inner_flag);
+        ypr_description(ctx, items[u].dsc, items[u].exts, &inner_flag);
+        ypr_reference(ctx, items[u].ref, items[u].exts, &inner_flag);
+        LEVEL--;
+        ypr_close(ctx, inner_flag);
+    }
+}
+
+static void
+yprp_type(struct ypr_ctx *ctx, const struct lysp_type *type)
+{
+    unsigned int u;
+    int flag = 0;
+
+    ly_print(ctx->out, "%*stype %s", INDENT, type->name);
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, type->exts, &flag, 0);
+
+    yprp_restr(ctx, type->range, "range", &flag);
+    yprp_restr(ctx, type->length, "length", &flag);
+    LY_ARRAY_FOR(type->patterns, u) {
+        yprp_restr(ctx, &type->patterns[u], "pattern", &flag);
+    }
+    yprp_enum(ctx, type->bits, LY_TYPE_BITS, &flag);
+    yprp_enum(ctx, type->enums, LY_TYPE_ENUM, &flag);
+
+    if (type->path) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_PATH, 0, type->path, type->exts);
+    }
+    if (type->flags & LYS_SET_REQINST) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_REQINSTANCE, 0, type->require_instance ? "true" : "false", type->exts);
+    }
+    if (type->flags & LYS_SET_FRDIGITS) {
+        ypr_unsigned(ctx, LYEXT_SUBSTMT_FRACDIGITS, 0, type->exts, type->fraction_digits, &flag);
+    }
+    LY_ARRAY_FOR(type->bases, u) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_BASE, u, type->bases[u], type->exts);
+    }
+    LY_ARRAY_FOR(type->types, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_type(ctx, &type->types[u]);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_type(struct ypr_ctx *ctx, const struct lysc_type *type)
+{
+    unsigned int u;
+    int flag = 0;
+
+    ly_print(ctx->out, "%*stype %s", INDENT, lys_datatype2str(type->basetype));
+    LEVEL++;
+
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, type->exts, &flag, 0);
+    if (type->dflt) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, 0, type->dflt, type->exts);
+    }
+
+    switch(type->basetype) {
+    case LY_TYPE_BINARY: {
+        struct lysc_type_bin *bin = (struct lysc_type_bin*)type;
+        yprc_range(ctx, bin->length, type->basetype, &flag);
+        break;
+    }
+    case LY_TYPE_UINT8:
+    case LY_TYPE_UINT16:
+    case LY_TYPE_UINT32:
+    case LY_TYPE_UINT64:
+    case LY_TYPE_INT8:
+    case LY_TYPE_INT16:
+    case LY_TYPE_INT32:
+    case LY_TYPE_INT64: {
+        struct lysc_type_num *num = (struct lysc_type_num*)type;
+        yprc_range(ctx, num->range, type->basetype, &flag);
+        break;
+    }
+    case LY_TYPE_STRING: {
+        struct lysc_type_str *str = (struct lysc_type_str*)type;
+        yprc_range(ctx, str->length, type->basetype, &flag);
+        LY_ARRAY_FOR(str->patterns, u) {
+            yprc_pattern(ctx, str->patterns[u], &flag);
+        }
+        break;
+    }
+    case LY_TYPE_BITS:
+    case LY_TYPE_ENUM: {
+        /* bits and enums structures are compatible */
+        struct lysc_type_bits *bits = (struct lysc_type_bits*)type;
+        LY_ARRAY_FOR(bits->bits, u) {
+            struct lysc_type_bitenum_item *item = &bits->bits[u];
+            int inner_flag = 0;
+
+            ypr_open(ctx->out, &flag);
+            ly_print(ctx->out, "%*s%s \"", INDENT, type->basetype == LY_TYPE_BITS ? "bit" : "enum");
+            ypr_encode(ctx->out, item->name, -1);
+            ly_print(ctx->out, "\"");
+            LEVEL++;
+            yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, item->exts, &inner_flag, 0);
+            yprc_iffeatures(ctx, item->iffeatures, item->exts, &inner_flag);
+            if (type->basetype == LY_TYPE_BITS) {
+                ypr_unsigned(ctx, LYEXT_SUBSTMT_POSITION, 0, item->exts, item->position, &inner_flag);
+            } else { /* LY_TYPE_ENUM */
+                ypr_signed(ctx, LYEXT_SUBSTMT_VALUE, 0, item->exts, item->value, &inner_flag);
+            }
+            ypr_status(ctx, item->flags, item->exts, &inner_flag);
+            ypr_description(ctx, item->dsc, item->exts, &inner_flag);
+            ypr_reference(ctx, item->ref, item->exts, &inner_flag);
+            LEVEL--;
+            ypr_close(ctx, inner_flag);
+        }
+        break;
+    }
+    case LY_TYPE_BOOL:
+    case LY_TYPE_EMPTY:
+        /* nothing to do */
+        break;
+    case LY_TYPE_DEC64: {
+        struct lysc_type_dec *dec = (struct lysc_type_dec*)type;
+        ypr_open(ctx->out, &flag);
+        ypr_unsigned(ctx, LYEXT_SUBSTMT_FRACDIGITS, 0, type->exts, dec->fraction_digits, &flag);
+        yprc_range(ctx, dec->range, dec->basetype, &flag);
+        break;
+    }
+    case LY_TYPE_IDENT: {
+        struct lysc_type_identityref *ident = (struct lysc_type_identityref*)type;
+        LY_ARRAY_FOR(ident->bases, u) {
+            ypr_open(ctx->out, &flag);
+            ypr_substmt(ctx, LYEXT_SUBSTMT_BASE, u, ident->bases[u]->name, type->exts);
+        }
+        break;
+    }
+    case LY_TYPE_INST: {
+        struct lysc_type_instanceid *inst = (struct lysc_type_instanceid*)type;
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_REQINSTANCE, 0, inst->require_instance ? "true" : "false", inst->exts);
+        break;
+    }
+    case LY_TYPE_LEAFREF: {
+        struct lysc_type_leafref *lr = (struct lysc_type_leafref*)type;
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_PATH, 0, lr->path, lr->exts);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_REQINSTANCE, 0, lr->require_instance ? "true" : "false", lr->exts);
+        yprc_type(ctx, lr->realtype);
+        break;
+    }
+    case LY_TYPE_UNION: {
+        struct lysc_type_union *un = (struct lysc_type_union*)type;
+        LY_ARRAY_FOR(un->types, u) {
+            ypr_open(ctx->out, &flag);
+            yprc_type(ctx, un->types[u]);
+        }
+        break;
+    }
+    default:
+        LOGINT(ctx->module->ctx);
+        ctx->out->status = LY_EINT;
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_typedef(struct ypr_ctx *ctx, const struct lysp_tpdf *tpdf)
+{
+    LYOUT_CHECK(ctx->out);
+
+    ly_print(ctx->out, "\n%*stypedef %s {\n", INDENT, tpdf->name);
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, tpdf->exts, NULL, 0);
+
+    yprp_type(ctx, &tpdf->type);
+
+    if (tpdf->units) {
+        ypr_substmt(ctx, LYEXT_SUBSTMT_UNITS, 0, tpdf->units, tpdf->exts);
+    }
+    if (tpdf->dflt) {
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, 0, tpdf->dflt, tpdf->exts);
+    }
+
+    ypr_status(ctx, tpdf->flags, tpdf->exts, NULL);
+    ypr_description(ctx, tpdf->dsc, tpdf->exts, NULL);
+    ypr_reference(ctx, tpdf->ref, tpdf->exts, NULL);
+
+    LEVEL--;
+    ly_print(ctx->out, "%*s}\n", INDENT);
+}
+
+static void yprp_node(struct ypr_ctx *ctx, const struct lysp_node *node);
+static void yprc_node(struct ypr_ctx *ctx, const struct lysc_node *node);
+static void yprp_action(struct ypr_ctx *ctx, const struct lysp_action *action);
+
+static void
+yprp_grouping(struct ypr_ctx *ctx, const struct lysp_grp *grp)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysp_node *data;
+
+    LYOUT_CHECK(ctx->out);
+
+    ly_print(ctx->out, "\n%*sgrouping %s", INDENT, grp->name);
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, grp->exts, &flag, 0);
+    ypr_status(ctx, grp->flags, grp->exts, &flag);
+    ypr_description(ctx, grp->dsc, grp->exts, &flag);
+    ypr_reference(ctx, grp->ref, grp->exts, &flag);
+
+    LY_ARRAY_FOR(grp->typedefs, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_typedef(ctx, &grp->typedefs[u]);
+    }
+
+    LY_ARRAY_FOR(grp->groupings, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_grouping(ctx, &grp->groupings[u]);
+    }
+
+    LY_LIST_FOR(grp->data, data) {
+        ypr_open(ctx->out, &flag);
+        yprp_node(ctx, data);
+    }
+
+    LY_ARRAY_FOR(grp->actions, u) {
+        yprp_action(ctx, &grp->actions[u]);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_inout(struct ypr_ctx *ctx, const struct lysp_action_inout *inout, int *flag)
+{
+    unsigned int u;
+    struct lysp_node *data;
+
+    if (!inout->nodetype) {
+        /* nodetype not set -> input/output is empty */
+        return;
+    }
+    ypr_open(ctx->out, flag);
+
+    ly_print(ctx->out, "\n%*s%s {\n", INDENT, (inout->nodetype == LYS_INPUT ? "input" : "output"));
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, inout->exts, NULL, 0);
+    LY_ARRAY_FOR(inout->musts, u) {
+        yprp_restr(ctx, &inout->musts[u], "must", NULL);
+    }
+    LY_ARRAY_FOR(inout->typedefs, u) {
+        yprp_typedef(ctx, &inout->typedefs[u]);
+    }
+    LY_ARRAY_FOR(inout->groupings, u) {
+        yprp_grouping(ctx, &inout->groupings[u]);
+    }
+
+    LY_LIST_FOR(inout->data, data) {
+        yprp_node(ctx, data);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, 1);
+}
+
+static void
+yprc_inout(struct ypr_ctx *ctx, const struct lysc_action *action, const struct lysc_action_inout *inout, int *flag)
+{
+    unsigned int u;
+    struct lysc_node *data;
+
+    if (!inout->data) {
+        /* input/output is empty */
+        return;
+    }
+    ypr_open(ctx->out, flag);
+
+    ly_print(ctx->out, "\n%*s%s {\n", INDENT, (&action->input == inout) ? "input" : "output");
+    LEVEL++;
+
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, (&action->input == inout) ? action->input_exts : action->output_exts, NULL, 0);
+    LY_ARRAY_FOR(inout->musts, u) {
+        yprc_must(ctx, &inout->musts[u], NULL);
+    }
+
+    LY_LIST_FOR(inout->data, data) {
+        yprc_node(ctx, data);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, 1);
+}
+
+static void
+yprp_notification(struct ypr_ctx *ctx, const struct lysp_notif *notif)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysp_node *data;
+
+    LYOUT_CHECK(ctx->out);
+
+    ly_print(ctx->out, "%*snotification %s", INDENT, notif->name);
+
+    LEVEL++;
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, notif->exts, &flag, 0);
+    yprp_iffeatures(ctx, notif->iffeatures, notif->exts, &flag);
+
+    LY_ARRAY_FOR(notif->musts, u) {
+        yprp_restr(ctx, &notif->musts[u], "must", &flag);
+    }
+    ypr_status(ctx, notif->flags, notif->exts, &flag);
+    ypr_description(ctx, notif->dsc, notif->exts, &flag);
+    ypr_reference(ctx, notif->ref, notif->exts, &flag);
+
+    LY_ARRAY_FOR(notif->typedefs, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_typedef(ctx, &notif->typedefs[u]);
+    }
+
+    LY_ARRAY_FOR(notif->groupings, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_grouping(ctx, &notif->groupings[u]);
+    }
+
+    LY_LIST_FOR(notif->data, data) {
+        ypr_open(ctx->out, &flag);
+        yprp_node(ctx, data);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_notification(struct ypr_ctx *ctx, const struct lysc_notif *notif)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysc_node *data;
+
+    LYOUT_CHECK(ctx->out);
+
+    ly_print(ctx->out, "%*snotification %s", INDENT, notif->name);
+
+    LEVEL++;
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, notif->exts, &flag, 0);
+    yprc_iffeatures(ctx, notif->iffeatures, notif->exts, &flag);
+
+    LY_ARRAY_FOR(notif->musts, u) {
+        yprc_must(ctx, &notif->musts[u], &flag);
+    }
+    ypr_status(ctx, notif->flags, notif->exts, &flag);
+    ypr_description(ctx, notif->dsc, notif->exts, &flag);
+    ypr_reference(ctx, notif->ref, notif->exts, &flag);
+
+    LY_LIST_FOR(notif->data, data) {
+        ypr_open(ctx->out, &flag);
+        yprc_node(ctx, data);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_action(struct ypr_ctx *ctx, const struct lysp_action *action)
+{
+    unsigned int u;
+    int flag = 0;
+
+    LYOUT_CHECK(ctx->out);
+
+    ly_print(ctx->out, "%*s%s %s", INDENT, action->parent ? "action" : "rpc", action->name);
+
+    LEVEL++;
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, action->exts, &flag, 0);
+    yprp_iffeatures(ctx, action->iffeatures, action->exts, &flag);
+    ypr_status(ctx, action->flags, action->exts, &flag);
+    ypr_description(ctx, action->dsc, action->exts, &flag);
+    ypr_reference(ctx, action->ref, action->exts, &flag);
+
+    LY_ARRAY_FOR(action->typedefs, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_typedef(ctx, &action->typedefs[u]);
+    }
+
+    LY_ARRAY_FOR(action->groupings, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_grouping(ctx, &action->groupings[u]);
+    }
+
+    yprp_inout(ctx, &action->input, &flag);
+    yprp_inout(ctx, &action->output, &flag);
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_action(struct ypr_ctx *ctx, const struct lysc_action *action)
+{
+    int flag = 0;
+
+    LYOUT_CHECK(ctx->out);
+
+    ly_print(ctx->out, "%*s%s %s", INDENT, action->parent ? "action" : "rpc", action->name);
+
+    LEVEL++;
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, action->exts, &flag, 0);
+    yprc_iffeatures(ctx, action->iffeatures, action->exts, &flag);
+    ypr_status(ctx, action->flags, action->exts, &flag);
+    ypr_description(ctx, action->dsc, action->exts, &flag);
+    ypr_reference(ctx, action->ref, action->exts, &flag);
+
+    yprc_inout(ctx, action, &action->input, &flag);
+    yprc_inout(ctx, action, &action->output, &flag);
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_node_common1(struct ypr_ctx *ctx, const struct lysp_node *node, int *flag)
+{
+    ly_print(ctx->out, "\n%*s%s %s%s", INDENT, lys_nodetype2str(node->nodetype), node->name, flag ? "" : " {\n");
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, node->exts, flag, 0);
+    yprp_when(ctx, node->when, flag);
+    yprp_iffeatures(ctx, node->iffeatures, node->exts, flag);
+}
+
+static void
+yprc_node_common1(struct ypr_ctx *ctx, const struct lysc_node *node, int *flag)
+{
+    unsigned int u;
+
+    ly_print(ctx->out, "\n%*s%s %s%s", INDENT, lys_nodetype2str(node->nodetype), node->name, flag ? "" : " {\n");
+    LEVEL++;
+
+    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, node->exts, flag, 0);
+    LY_ARRAY_FOR(node->when, u) {
+        yprc_when(ctx, node->when[u], flag);
+    }
+    yprc_iffeatures(ctx, node->iffeatures, node->exts, flag);
+}
+
+/* macr oto unify the code */
+#define YPR_NODE_COMMON2 \
+    ypr_config(ctx, node->flags, node->exts, flag); \
+    if (node->nodetype & (LYS_CHOICE | LYS_LEAF | LYS_ANYDATA)) { \
+        ypr_mandatory(ctx, node->flags, node->exts, flag); \
+    } \
+    ypr_status(ctx, node->flags, node->exts, flag); \
+    ypr_description(ctx, node->dsc, node->exts, flag); \
+    ypr_reference(ctx, node->ref, node->exts, flag)
+
+static void
+yprp_node_common2(struct ypr_ctx *ctx, const struct lysp_node *node, int *flag)
+{
+    YPR_NODE_COMMON2;
+}
+
+static void
+yprc_node_common2(struct ypr_ctx *ctx, const struct lysc_node *node, int *flag)
+{
+    YPR_NODE_COMMON2;
+}
+
+#undef YPR_NODE_COMMON2
+
+static void
+yprp_container(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysp_node *child;
+    struct lysp_node_container *cont = (struct lysp_node_container *)node;
+
+    yprp_node_common1(ctx, node, &flag);
+
+    LY_ARRAY_FOR(cont->musts, u) {
+        yprp_restr(ctx, &cont->musts[u], "must", &flag);
+    }
+    if (cont->presence) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_PRESENCE, 0, cont->presence, cont->exts);
+    }
+
+    yprp_node_common2(ctx, node, &flag);
+
+    LY_ARRAY_FOR(cont->typedefs, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_typedef(ctx, &cont->typedefs[u]);
+    }
+
+    LY_ARRAY_FOR(cont->groupings, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_grouping(ctx, &cont->groupings[u]);
+    }
+
+    LY_LIST_FOR(cont->child, child) {
+        ypr_open(ctx->out, &flag);
+        yprp_node(ctx, child);
+    }
+
+    LY_ARRAY_FOR(cont->actions, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_action(ctx, &cont->actions[u]);
+    }
+
+    LY_ARRAY_FOR(cont->notifs, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_notification(ctx, &cont->notifs[u]);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_container(struct ypr_ctx *ctx, const struct lysc_node *node)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysc_node *child;
+    struct lysc_node_container *cont = (struct lysc_node_container *)node;
+
+    yprc_node_common1(ctx, node, &flag);
+
+    LY_ARRAY_FOR(cont->musts, u) {
+        yprc_must(ctx, &cont->musts[u], &flag);
+    }
+    if (cont->flags & LYS_PRESENCE) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_PRESENCE, 0, "true", cont->exts);
+    }
+
+    yprc_node_common2(ctx, node, &flag);
+
+    LY_LIST_FOR(cont->child, child) {
+        ypr_open(ctx->out, &flag);
+        yprc_node(ctx, child);
+    }
+
+    LY_ARRAY_FOR(cont->actions, u) {
+        ypr_open(ctx->out, &flag);
+        yprc_action(ctx, &cont->actions[u]);
+    }
+
+    LY_ARRAY_FOR(cont->notifs, u) {
+        ypr_open(ctx->out, &flag);
+        yprc_notification(ctx, &cont->notifs[u]);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_case(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    int flag = 0;
+    struct lysp_node *child;
+    struct lysp_node_case *cas = (struct lysp_node_case *)node;
+
+    yprp_node_common1(ctx, node, &flag);
+    yprp_node_common2(ctx, node, &flag);
+
+    LY_LIST_FOR(cas->child, child) {
+        ypr_open(ctx->out, &flag);
+        yprp_node(ctx, child);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_case(struct ypr_ctx *ctx, const struct lysc_node_case *cs)
+{
+    int flag = 0;
+    struct lysc_node *child;
+
+    yprc_node_common1(ctx, (struct lysc_node*)cs, &flag);
+    yprc_node_common2(ctx, (struct lysc_node*)cs, &flag);
+
+    for (child = cs->child; child && child->parent == (struct lysc_node*)cs; child = child->next) {
+        ypr_open(ctx->out, &flag);
+        yprc_node(ctx, child);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_choice(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    int flag = 0;
+    struct lysp_node *child;
+    struct lysp_node_choice *choice = (struct lysp_node_choice *)node;
+
+    yprp_node_common1(ctx, node, &flag);
+
+    if (choice->dflt) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, 0, choice->dflt, choice->exts);
+    }
+
+    yprp_node_common2(ctx, node, &flag);
+
+    LY_LIST_FOR(choice->child, child) {
+        ypr_open(ctx->out, &flag);
+        yprp_node(ctx, child);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_choice(struct ypr_ctx *ctx, const struct lysc_node *node)
+{
+    int flag = 0;
+    struct lysc_node_case *cs;
+    struct lysc_node_choice *choice = (struct lysc_node_choice *)node;
+
+    yprc_node_common1(ctx, node, &flag);
+
+    if (choice->dflt) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, 0, choice->dflt->name, choice->exts);
+    }
+
+    yprc_node_common2(ctx, node, &flag);
+
+    for (cs = choice->cases; cs; cs = (struct lysc_node_case*)cs->next) {
+        ypr_open(ctx->out, &flag);
+        yprc_case(ctx, cs);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_leaf(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    unsigned int u;
+    struct lysp_node_leaf *leaf = (struct lysp_node_leaf *)node;
+
+    yprp_node_common1(ctx, node, NULL);
+
+    yprp_type(ctx, &leaf->type);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_UNITS, 0, leaf->units, leaf->exts);
+    LY_ARRAY_FOR(leaf->musts, u) {
+        yprp_restr(ctx, &leaf->musts[u], "must", NULL);
+    }
+    ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, 0, leaf->dflt, leaf->exts);
+
+    yprp_node_common2(ctx, node, NULL);
+
+    LEVEL--;
+    ly_print(ctx->out, "%*s}\n", INDENT);
+}
+
+static void
+yprc_leaf(struct ypr_ctx *ctx, const struct lysc_node *node)
+{
+    unsigned int u;
+    struct lysc_node_leaf *leaf = (struct lysc_node_leaf *)node;
+
+    yprc_node_common1(ctx, node, NULL);
+
+    yprc_type(ctx, leaf->type);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_UNITS, 0, leaf->units, leaf->exts);
+    LY_ARRAY_FOR(leaf->musts, u) {
+        yprc_must(ctx, &leaf->musts[u], NULL);
+    }
+    ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, 0, leaf->dflt, leaf->exts);
+
+    yprc_node_common2(ctx, node, NULL);
+
+    LEVEL--;
+    ly_print(ctx->out, "%*s}\n", INDENT);
+}
+
+static void
+yprp_leaflist(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    unsigned int u;
+    struct lysp_node_leaflist *llist = (struct lysp_node_leaflist *)node;
+
+    yprp_node_common1(ctx, node, NULL);
+
+    yprp_type(ctx, &llist->type);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_UNITS, 0, llist->units, llist->exts);
+    LY_ARRAY_FOR(llist->musts, u) {
+        yprp_restr(ctx, &llist->musts[u], "must", NULL);
+    }
+    LY_ARRAY_FOR(llist->dflts, u) {
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, u, llist->dflts[u], llist->exts);
+    }
+
+    ypr_config(ctx, node->flags, node->exts, NULL);
+
+    if (llist->flags & LYS_SET_MIN) {
+        ypr_unsigned(ctx, LYEXT_SUBSTMT_MIN, 0, llist->exts, llist->min, NULL);
+    }
+    if (llist->flags & LYS_SET_MAX) {
+        if (llist->max) {
+            ypr_unsigned(ctx, LYEXT_SUBSTMT_MAX, 0, llist->exts, llist->max, NULL);
+        } else {
+            ypr_substmt(ctx, LYEXT_SUBSTMT_MAX, 0, "unbounded", llist->exts);
+        }
+    }
+
+    if (llist->flags & LYS_ORDBY_MASK) {
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ORDEREDBY, 0, (llist->flags & LYS_ORDBY_USER) ? "user" : "system", llist->exts);
+    }
+
+    ypr_status(ctx, node->flags, node->exts, NULL);
+    ypr_description(ctx, node->dsc, node->exts, NULL);
+    ypr_reference(ctx, node->ref, node->exts, NULL);
+
+    LEVEL--;
+    ly_print(ctx->out, "%*s}\n", INDENT);
+}
+
+static void
+yprc_leaflist(struct ypr_ctx *ctx, const struct lysc_node *node)
+{
+    unsigned int u;
+    struct lysc_node_leaflist *llist = (struct lysc_node_leaflist *)node;
+
+    yprc_node_common1(ctx, node, NULL);
+
+    yprc_type(ctx, llist->type);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_UNITS, 0, llist->units, llist->exts);
+    LY_ARRAY_FOR(llist->musts, u) {
+        yprc_must(ctx, &llist->musts[u], NULL);
+    }
+    LY_ARRAY_FOR(llist->dflts, u) {
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, u, llist->dflts[u], llist->exts);
+    }
+
+    ypr_config(ctx, node->flags, node->exts, NULL);
+
+    ypr_unsigned(ctx, LYEXT_SUBSTMT_MIN, 0, llist->exts, llist->min, NULL);
+    if (llist->max) {
+        ypr_unsigned(ctx, LYEXT_SUBSTMT_MAX, 0, llist->exts, llist->max, NULL);
+    } else {
+        ypr_substmt(ctx, LYEXT_SUBSTMT_MAX, 0, "unbounded", llist->exts);
+    }
+
+    ypr_substmt(ctx, LYEXT_SUBSTMT_ORDEREDBY, 0, (llist->flags & LYS_ORDBY_USER) ? "user" : "system", llist->exts);
+
+    ypr_status(ctx, node->flags, node->exts, NULL);
+    ypr_description(ctx, node->dsc, node->exts, NULL);
+    ypr_reference(ctx, node->ref, node->exts, NULL);
+
+    LEVEL--;
+    ly_print(ctx->out, "%*s}\n", INDENT);
+}
+
+static void
+yprp_list(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysp_node *child;
+    struct lysp_node_list *list = (struct lysp_node_list *)node;
+
+    yprp_node_common1(ctx, node, &flag);
+
+    LY_ARRAY_FOR(list->musts, u) {
+        yprp_restr(ctx, &list->musts[u], "must", NULL);
+    }
+    if (list->key) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_KEY, 0, list->key, list->exts);
+    }
+    LY_ARRAY_FOR(list->uniques, u) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_UNIQUE, u, list->uniques[u], list->exts);
+    }
+
+    ypr_config(ctx, node->flags, node->exts, NULL);
+
+    if (list->flags & LYS_SET_MIN) {
+        ypr_unsigned(ctx, LYEXT_SUBSTMT_MIN, 0, list->exts, list->min, NULL);
+    }
+    if (list->flags & LYS_SET_MAX) {
+        if (list->max) {
+            ypr_unsigned(ctx, LYEXT_SUBSTMT_MAX, 0, list->exts, list->max, NULL);
+        } else {
+            ypr_substmt(ctx, LYEXT_SUBSTMT_MAX, 0, "unbounded", list->exts);
+        }
+    }
+
+    if (list->flags & LYS_ORDBY_MASK) {
+        ypr_substmt(ctx, LYEXT_SUBSTMT_ORDEREDBY, 0, (list->flags & LYS_ORDBY_USER) ? "user" : "system", list->exts);
+    }
+
+    ypr_status(ctx, node->flags, node->exts, NULL);
+    ypr_description(ctx, node->dsc, node->exts, NULL);
+    ypr_reference(ctx, node->ref, node->exts, NULL);
+
+    LY_ARRAY_FOR(list->typedefs, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_typedef(ctx, &list->typedefs[u]);
+    }
+
+    LY_ARRAY_FOR(list->groupings, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_grouping(ctx, &list->groupings[u]);
+    }
+
+    LY_LIST_FOR(list->child, child) {
+        ypr_open(ctx->out, &flag);
+        yprp_node(ctx, child);
+    }
+
+    LY_ARRAY_FOR(list->actions, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_action(ctx, &list->actions[u]);
+    }
+
+    LY_ARRAY_FOR(list->notifs, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_notification(ctx, &list->notifs[u]);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_list(struct ypr_ctx *ctx, const struct lysc_node *node)
+{
+    unsigned int u, v;
+    int flag = 0;
+    struct lysc_node *child;
+    struct lysc_node_list *list = (struct lysc_node_list *)node;
+
+    yprc_node_common1(ctx, node, &flag);
+
+    LY_ARRAY_FOR(list->musts, u) {
+        yprc_must(ctx, &list->musts[u], NULL);
+    }
+    if (list->keys) {
+        ypr_open(ctx->out, &flag);
+        ly_print(ctx->out, "%*skey \"", INDENT);
+        LY_ARRAY_FOR(list->keys, u) {
+            ly_print(ctx->out, "%s%s", u > 0 ? ", " : "", list->keys[u]->name);
+        }
+        ypr_close(ctx, 0);
+    }
+    LY_ARRAY_FOR(list->uniques, u) {
+        ypr_open(ctx->out, &flag);
+        ly_print(ctx->out, "%*sunique \"", INDENT);
+        LY_ARRAY_FOR(list->uniques[u], v) {
+            ly_print(ctx->out, "%s%s", v > 0 ? ", " : "", list->uniques[u][v]->name);
+        }
+        ypr_close(ctx, 0);
+    }
+
+    ypr_config(ctx, node->flags, node->exts, NULL);
+
+    ypr_unsigned(ctx, LYEXT_SUBSTMT_MIN, 0, list->exts, list->min, NULL);
+    if (list->max) {
+        ypr_unsigned(ctx, LYEXT_SUBSTMT_MAX, 0, list->exts, list->max, NULL);
+    } else {
+        ypr_substmt(ctx, LYEXT_SUBSTMT_MAX, 0, "unbounded", list->exts);
+    }
+
+    ypr_substmt(ctx, LYEXT_SUBSTMT_ORDEREDBY, 0, (list->flags & LYS_ORDBY_USER) ? "user" : "system", list->exts);
+
+    ypr_status(ctx, node->flags, node->exts, NULL);
+    ypr_description(ctx, node->dsc, node->exts, NULL);
+    ypr_reference(ctx, node->ref, node->exts, NULL);
+
+    LY_LIST_FOR(list->child, child) {
+        ypr_open(ctx->out, &flag);
+        yprc_node(ctx, child);
+    }
+
+    LY_ARRAY_FOR(list->actions, u) {
+        ypr_open(ctx->out, &flag);
+        yprc_action(ctx, &list->actions[u]);
+    }
+
+    LY_ARRAY_FOR(list->notifs, u) {
+        ypr_open(ctx->out, &flag);
+        yprc_notification(ctx, &list->notifs[u]);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_refine(struct ypr_ctx *ctx, struct lysp_refine *refine)
+{
+    unsigned int u;
+    int flag = 0;
+
+    ly_print(ctx->out, "%*srefine \"%s\"", INDENT, refine->nodeid);
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, refine->exts, &flag, 0);
+    yprp_iffeatures(ctx, refine->iffeatures, refine->exts, &flag);
+
+    LY_ARRAY_FOR(refine->musts, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_restr(ctx, &refine->musts[u], "must", NULL);
+    }
+
+    if (refine->presence) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_PRESENCE, 0, refine->presence, refine->exts);
+    }
+
+    LY_ARRAY_FOR(refine->dflts, u) {
+        ypr_open(ctx->out, &flag);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, u, refine->dflts[u], refine->exts);
+    }
+
+    ypr_config(ctx, refine->flags, refine->exts, &flag);
+    ypr_mandatory(ctx, refine->flags, refine->exts, &flag);
+
+    if (refine->flags & LYS_SET_MIN) {
+        ypr_open(ctx->out, &flag);
+        ypr_unsigned(ctx, LYEXT_SUBSTMT_MIN, 0, refine->exts, refine->min, NULL);
+    }
+    if (refine->flags & LYS_SET_MAX) {
+        ypr_open(ctx->out, &flag);
+        if (refine->max) {
+            ypr_unsigned(ctx, LYEXT_SUBSTMT_MAX, 0, refine->exts, refine->max, NULL);
+        } else {
+            ypr_substmt(ctx, LYEXT_SUBSTMT_MAX, 0, "unbounded", refine->exts);
+        }
+    }
+
+    ypr_description(ctx, refine->dsc, refine->exts, &flag);
+    ypr_reference(ctx, refine->ref, refine->exts, &flag);
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_augment(struct ypr_ctx *ctx, const struct lysp_augment *aug)
+{
+    unsigned int u;
+    struct lysp_node *child;
+
+    ly_print(ctx->out, "%*saugment \"%s\" {\n", INDENT, aug->nodeid);
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, aug->exts, NULL, 0);
+    yprp_when(ctx, aug->when, NULL);
+    yprp_iffeatures(ctx, aug->iffeatures, aug->exts, NULL);
+    ypr_status(ctx, aug->flags, aug->exts, NULL);
+    ypr_description(ctx, aug->dsc, aug->exts, NULL);
+    ypr_reference(ctx, aug->ref, aug->exts, NULL);
+
+    LY_LIST_FOR(aug->child, child) {
+        yprp_node(ctx, child);
+    }
+
+    LY_ARRAY_FOR(aug->actions, u) {
+        yprp_action(ctx, &aug->actions[u]);
+    }
+
+    LY_ARRAY_FOR(aug->notifs, u) {
+        yprp_notification(ctx, &aug->notifs[u]);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, 1);
+}
+
+
+static void
+yprp_uses(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysp_node_uses *uses = (struct lysp_node_uses *)node;
+
+    yprp_node_common1(ctx, node, &flag);
+    yprp_node_common2(ctx, node, &flag);
+
+    LY_ARRAY_FOR(uses->refines, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_refine(ctx, &uses->refines[u]);
+    }
+
+    LY_ARRAY_FOR(uses->augments, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_augment(ctx, &uses->augments[u]);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_anydata(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysp_node_anydata *any = (struct lysp_node_anydata *)node;
+
+    yprp_node_common1(ctx, node, &flag);
+
+    LY_ARRAY_FOR(any->musts, u) {
+        ypr_open(ctx->out, &flag);
+        yprp_restr(ctx, &any->musts[u], "must", NULL);
+    }
+
+    yprp_node_common2(ctx, node, &flag);
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprc_anydata(struct ypr_ctx *ctx, const struct lysc_node *node)
+{
+    unsigned int u;
+    int flag = 0;
+    struct lysc_node_anydata *any = (struct lysc_node_anydata *)node;
+
+    yprc_node_common1(ctx, node, &flag);
+
+    LY_ARRAY_FOR(any->musts, u) {
+        ypr_open(ctx->out, &flag);
+        yprc_must(ctx, &any->musts[u], NULL);
+    }
+
+    yprc_node_common2(ctx, node, &flag);
+
+    LEVEL--;
+    ypr_close(ctx, flag);
+}
+
+static void
+yprp_node(struct ypr_ctx *ctx, const struct lysp_node *node)
+{
+    LYOUT_CHECK(ctx->out);
+
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        yprp_container(ctx, node);
+        break;
+    case LYS_CHOICE:
+        yprp_choice(ctx, node);
+        break;
+    case LYS_LEAF:
+        yprp_leaf(ctx, node);
+        break;
+    case LYS_LEAFLIST:
+        yprp_leaflist(ctx, node);
+        break;
+    case LYS_LIST:
+        yprp_list(ctx, node);
+        break;
+    case LYS_USES:
+        yprp_uses(ctx, node);
+        break;
+    case LYS_ANYXML:
+    case LYS_ANYDATA:
+        yprp_anydata(ctx, node);
+        break;
+    case LYS_CASE:
+        yprp_case(ctx, node);
+        break;
+    default:
+        break;
+    }
+}
+
+static void
+yprc_node(struct ypr_ctx *ctx, const struct lysc_node *node)
+{
+    LYOUT_CHECK(ctx->out);
+
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        yprc_container(ctx, node);
+        break;
+    case LYS_CHOICE:
+        yprc_choice(ctx, node);
+        break;
+    case LYS_LEAF:
+        yprc_leaf(ctx, node);
+        break;
+    case LYS_LEAFLIST:
+        yprc_leaflist(ctx, node);
+        break;
+    case LYS_LIST:
+        yprc_list(ctx, node);
+        break;
+    case LYS_ANYXML:
+    case LYS_ANYDATA:
+        yprc_anydata(ctx, node);
+        break;
+    default:
+        break;
+    }
+}
+
+static void
+yprp_deviation(struct ypr_ctx *ctx, const struct lysp_deviation *deviation)
+{
+    unsigned int u, v;
+    struct lysp_deviate_add *add;
+    struct lysp_deviate_rpl *rpl;
+    struct lysp_deviate_del *del;
+
+    ly_print(ctx->out, "%*sdeviation \"%s\" {\n", INDENT, deviation->nodeid);
+    LEVEL++;
+
+    yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, deviation->exts, NULL, 0);
+    ypr_description(ctx, deviation->dsc, deviation->exts, NULL);
+    ypr_reference(ctx, deviation->ref, deviation->exts, NULL);
+
+    LY_ARRAY_FOR(deviation->deviates, u) {
+        ly_print(ctx->out, "%*sdeviate ", INDENT);
+        if (deviation->deviates[u].mod == LYS_DEV_NOT_SUPPORTED) {
+            if (deviation->deviates[u].exts) {
+                ly_print(ctx->out, "not-supported {\n");
+                LEVEL++;
+
+                yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, deviation->deviates[u].exts, NULL, 0);
+            } else {
+                ly_print(ctx->out, "not-supported;\n");
+                continue;
+            }
+        } else if (deviation->deviates[u].mod == LYS_DEV_ADD) {
+            add = (struct lysp_deviate_add*)&deviation->deviates[u];
+            ly_print(ctx->out, "add {\n");
+            LEVEL++;
+
+            yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, add->exts, NULL, 0);
+            ypr_substmt(ctx, LYEXT_SUBSTMT_UNITS, 0, add->units, add->exts);
+            LY_ARRAY_FOR(add->musts, v) {
+                yprp_restr(ctx, &add->musts[v], "must", NULL);
+            }
+            LY_ARRAY_FOR(add->uniques, v) {
+                ypr_substmt(ctx, LYEXT_SUBSTMT_UNIQUE, v, add->uniques[v], add->exts);
+            }
+            LY_ARRAY_FOR(add->dflts, v) {
+                ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, v, add->dflts[v], add->exts);
+            }
+            ypr_config(ctx, add->flags, add->exts, NULL);
+            ypr_mandatory(ctx, add->flags, add->exts, NULL);
+            if (add->flags & LYS_SET_MIN) {
+                ypr_unsigned(ctx, LYEXT_SUBSTMT_MIN, 0, add->exts, add->min, NULL);
+            }
+            if (add->flags & LYS_SET_MAX) {
+                if (add->max) {
+                    ypr_unsigned(ctx, LYEXT_SUBSTMT_MAX, 0, add->exts, add->max, NULL);
+                } else {
+                    ypr_substmt(ctx, LYEXT_SUBSTMT_MAX, 0, "unbounded", add->exts);
+                }
+            }
+        } else if (deviation->deviates[u].mod == LYS_DEV_REPLACE) {
+            rpl = (struct lysp_deviate_rpl*)&deviation->deviates[u];
+            ly_print(ctx->out, "replace {\n");
+            LEVEL++;
+
+            yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, rpl->exts, NULL, 0);
+            if (rpl->type) {
+                yprp_type(ctx, rpl->type);
+            }
+            ypr_substmt(ctx, LYEXT_SUBSTMT_UNITS, 0, rpl->units, rpl->exts);
+            ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, 0, rpl->dflt, rpl->exts);
+            ypr_config(ctx, rpl->flags, rpl->exts, NULL);
+            ypr_mandatory(ctx, rpl->flags, rpl->exts, NULL);
+            if (rpl->flags & LYS_SET_MIN) {
+                ypr_unsigned(ctx, LYEXT_SUBSTMT_MIN, 0, rpl->exts, rpl->min, NULL);
+            }
+            if (rpl->flags & LYS_SET_MAX) {
+                if (rpl->max) {
+                    ypr_unsigned(ctx, LYEXT_SUBSTMT_MAX, 0, rpl->exts, rpl->max, NULL);
+                } else {
+                    ypr_substmt(ctx, LYEXT_SUBSTMT_MAX, 0, "unbounded", rpl->exts);
+                }
+            }
+        } else if (deviation->deviates[u].mod == LYS_DEV_DELETE) {
+            del = (struct lysp_deviate_del*)&deviation->deviates[u];
+            ly_print(ctx->out, "delete {\n");
+            LEVEL++;
+
+            yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, del->exts, NULL, 0);
+            ypr_substmt(ctx, LYEXT_SUBSTMT_UNITS, 0, del->units, del->exts);
+            LY_ARRAY_FOR(del->musts, v) {
+                yprp_restr(ctx, &del->musts[v], "must", NULL);
+            }
+            LY_ARRAY_FOR(del->uniques, v) {
+                ypr_substmt(ctx, LYEXT_SUBSTMT_UNIQUE, v, del->uniques[v], del->exts);
+            }
+            LY_ARRAY_FOR(del->dflts, v) {
+                ypr_substmt(ctx, LYEXT_SUBSTMT_DEFAULT, v, del->dflts[v], del->exts);
+            }
+        }
+
+        LEVEL--;
+        ypr_close(ctx, 1);
+    }
+
+    LEVEL--;
+    ypr_close(ctx, 1);
+}
+
+/**
+ * @brief Minimal print of a schema.
+ *
+ * To print
+ * a) compiled schema when it is not compiled or
+ * b) parsed when the parsed form was already removed
+ */
+static LY_ERR
+ypr_missing_format(struct ypr_ctx *ctx, const struct lys_module *module)
+{
+    /* module-header-stmts */
+    if (module->version) {
+        if (module->version) {
+            ypr_substmt(ctx, LYEXT_SUBSTMT_VERSION, 0, module->version == LYS_VERSION_1_1 ? "1.1" : "1", NULL);
+        }
+    }
+    ypr_substmt(ctx, LYEXT_SUBSTMT_NAMESPACE, 0, module->ns, NULL);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_PREFIX, 0, module->prefix, NULL);
+
+    /* meta-stmts */
+    if (module->org || module->contact || module->dsc || module->ref) {
+        ly_print(ctx->out, "\n");
+    }
+    ypr_substmt(ctx, LYEXT_SUBSTMT_ORGANIZATION, 0, module->org, NULL);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_CONTACT, 0, module->contact, NULL);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_DESCRIPTION, 0, module->dsc, NULL);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_REFERENCE, 0, module->ref, NULL);
+
+    /* revision-stmts */
+    if (module->revision) {
+        ly_print(ctx->out, "\n%*srevision %s;\n", INDENT, module->revision);
+    }
+
+    LEVEL--;
+    ly_print(ctx->out, "%*s}\n", INDENT);
+    ly_print_flush(ctx->out);
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+yang_print_parsed(struct lyout *out, const struct lys_module *module)
+{
+    unsigned int u;
+    struct lysp_node *data;
+    struct lysp_module *modp = module->parsed;
+    struct ypr_ctx ctx_ = {.out = out, .level = 0, .module = module, .schema = YPR_PARSED}, *ctx = &ctx_;
+
+    ly_print(ctx->out, "%*smodule %s {\n", INDENT, module->name);
+    LEVEL++;
+
+    if (!modp) {
+        ly_print(ctx->out, "%*s/* PARSED INFORMATION ARE NOT FULLY PRESENT */\n", INDENT);
+        return ypr_missing_format(ctx, module);
+    }
+
+    /* module-header-stmts */
+    if (module->version) {
+        if (module->version) {
+            ypr_substmt(ctx, LYEXT_SUBSTMT_VERSION, 0, module->version == LYS_VERSION_1_1 ? "1.1" : "1", modp->exts);
+        }
+    }
+    ypr_substmt(ctx, LYEXT_SUBSTMT_NAMESPACE, 0, module->ns, modp->exts);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_PREFIX, 0, module->prefix, modp->exts);
+
+    /* linkage-stmts */
+    LY_ARRAY_FOR(modp->imports, u) {
+        ly_print(out, "\n%*simport %s {\n", INDENT, modp->imports[u].module->name);
+        LEVEL++;
+        yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, modp->imports[u].exts, NULL, 0);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_PREFIX, 0, modp->imports[u].prefix, modp->imports[u].exts);
+        if (modp->imports[u].rev[0]) {
+            ypr_substmt(ctx, LYEXT_SUBSTMT_REVISIONDATE, 0, modp->imports[u].rev, modp->imports[u].exts);
+        }
+        ypr_substmt(ctx, LYEXT_SUBSTMT_DESCRIPTION, 0, modp->imports[u].dsc, modp->imports[u].exts);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_REFERENCE, 0, modp->imports[u].ref, modp->imports[u].exts);
+        LEVEL--;
+        ly_print(out, "%*s}\n", INDENT);
+    }
+    LY_ARRAY_FOR(modp->includes, u) {
+        if (modp->includes[u].rev[0] || modp->includes[u].dsc || modp->includes[u].ref || modp->includes[u].exts) {
+            ly_print(out, "\n%*sinclude %s {\n", INDENT, modp->includes[u].submodule->name);
+            LEVEL++;
+            yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, modp->includes[u].exts, NULL, 0);
+            if (modp->includes[u].rev[0]) {
+                ypr_substmt(ctx, LYEXT_SUBSTMT_REVISIONDATE, 0, modp->includes[u].rev, modp->includes[u].exts);
+            }
+            ypr_substmt(ctx, LYEXT_SUBSTMT_DESCRIPTION, 0, modp->includes[u].dsc, modp->includes[u].exts);
+            ypr_substmt(ctx, LYEXT_SUBSTMT_REFERENCE, 0, modp->includes[u].ref, modp->includes[u].exts);
+            LEVEL--;
+            ly_print(out, "%*s}\n", INDENT);
+        } else {
+            ly_print(out, "\n%*sinclude \"%s\";\n", INDENT, modp->includes[u].submodule->name);
+        }
+    }
+
+    /* meta-stmts */
+    if (module->org || module->contact || module->dsc || module->ref) {
+        ly_print(out, "\n");
+    }
+    ypr_substmt(ctx, LYEXT_SUBSTMT_ORGANIZATION, 0, module->org, modp->exts);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_CONTACT, 0, module->contact, modp->exts);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_DESCRIPTION, 0, module->dsc, modp->exts);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_REFERENCE, 0, module->ref, modp->exts);
+
+    /* revision-stmts */
+    if (modp->revs) {
+        ly_print(out, "\n");
+    }
+    LY_ARRAY_FOR(modp->revs, u) {
+        yprp_revision(ctx, &modp->revs[u]);
+    }
+    /* body-stmts */
+    LY_ARRAY_FOR(modp->extensions, u) {
+        ly_print(out, "\n");
+        yprp_extension(ctx, &modp->extensions[u]);
+    }
+    if (modp->exts) {
+        ly_print(out, "\n");
+        yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, module->parsed->exts, NULL, 0);
+    }
+
+    LY_ARRAY_FOR(modp->features, u) {
+        yprp_feature(ctx, &modp->features[u]);
+    }
+
+    LY_ARRAY_FOR(modp->identities, u) {
+        yprp_identity(ctx, &modp->identities[u]);
+    }
+
+    LY_ARRAY_FOR(modp->typedefs, u) {
+        yprp_typedef(ctx, &modp->typedefs[u]);
+    }
+
+    LY_ARRAY_FOR(modp->groupings, u) {
+        yprp_grouping(ctx, &modp->groupings[u]);
+    }
+
+    LY_LIST_FOR(modp->data, data) {
+        yprp_node(ctx, data);
+    }
+
+    LY_ARRAY_FOR(modp->augments, u) {
+        yprp_augment(ctx, &modp->augments[u]);
+    }
+
+    LY_ARRAY_FOR(modp->rpcs, u) {
+        yprp_action(ctx, &modp->rpcs[u]);
+    }
+
+    LY_ARRAY_FOR(modp->notifs, u) {
+        yprp_notification(ctx, &modp->notifs[u]);
+    }
+
+    LY_ARRAY_FOR(modp->deviations, u) {
+        yprp_deviation(ctx, &modp->deviations[u]);
+    }
+
+    LEVEL--;
+    ly_print(out, "%*s}\n", INDENT);
+    ly_print_flush(out);
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+yang_print_compiled(struct lyout *out, const struct lys_module *module)
+{
+    unsigned int u;
+    struct lysc_node *data;
+    struct lysc_module *modc = module->compiled;
+    struct ypr_ctx ctx_ = {.out = out, .level = 0, .module = module}, *ctx = &ctx_;
+
+    ly_print(ctx->out, "%*smodule %s {\n", INDENT, module->name);
+    LEVEL++;
+
+    if (!modc) {
+        ly_print(ctx->out, "%*s/* COMPILED INFORMATION ARE NOT PRESENT */\n", INDENT);
+        return ypr_missing_format(ctx, module);
+    }
+
+    /* module-header-stmts */
+    if (module->version) {
+        if (module->version) {
+            ypr_substmt(ctx, LYEXT_SUBSTMT_VERSION, 0, module->version == LYS_VERSION_1_1 ? "1.1" : "1", modc->exts);
+        }
+    }
+    ypr_substmt(ctx, LYEXT_SUBSTMT_NAMESPACE, 0, module->ns, modc->exts);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_PREFIX, 0, module->prefix, modc->exts);
+
+    /* linkage-stmts */
+    LY_ARRAY_FOR(modc->imports, u) {
+        ly_print(out, "\n%*simport %s {\n", INDENT, modc->imports[u].module->name);
+        LEVEL++;
+        yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, modc->imports[u].exts, NULL, 0);
+        ypr_substmt(ctx, LYEXT_SUBSTMT_PREFIX, 0, modc->imports[u].prefix, modc->imports[u].exts);
+        if (modc->imports[u].module->revision) {
+            ypr_substmt(ctx, LYEXT_SUBSTMT_REVISIONDATE, 0, modc->imports[u].module->revision, modc->imports[u].exts);
+        }
+        LEVEL--;
+        ly_print(out, "%*s}\n", INDENT);
+    }
+
+    /* meta-stmts */
+    if (module->org || module->contact || module->dsc || module->ref) {
+        ly_print(out, "\n");
+    }
+    ypr_substmt(ctx, LYEXT_SUBSTMT_ORGANIZATION, 0, module->org, modc->exts);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_CONTACT, 0, module->contact, modc->exts);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_DESCRIPTION, 0, module->dsc, modc->exts);
+    ypr_substmt(ctx, LYEXT_SUBSTMT_REFERENCE, 0, module->ref, modc->exts);
+
+    /* revision-stmts */
+    if (module->revision) {
+        ly_print(ctx->out, "\n%*srevision %s;\n", INDENT, module->revision);
+    }
+
+    /* body-stmts */
+    if (modc->exts) {
+        ly_print(out, "\n");
+        yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, module->compiled->exts, NULL, 0);
+    }
+
+    LY_ARRAY_FOR(modc->features, u) {
+        yprc_feature(ctx, &modc->features[u]);
+    }
+
+    LY_ARRAY_FOR(modc->identities, u) {
+        yprc_identity(ctx, &modc->identities[u]);
+    }
+
+    LY_LIST_FOR(modc->data, data) {
+        yprc_node(ctx, data);
+    }
+
+    LY_ARRAY_FOR(modc->rpcs, u) {
+        yprc_action(ctx, &modc->rpcs[u]);
+    }
+
+    LY_ARRAY_FOR(modc->notifs, u) {
+        yprc_notification(ctx, &modc->notifs[u]);
+    }
+
+    LEVEL--;
+    ly_print(out, "%*s}\n", INDENT);
+    ly_print_flush(out);
+
+    return LY_SUCCESS;
+}
diff --git a/src/set.c b/src/set.c
index 7e567eb..3b7f078 100644
--- a/src/set.c
+++ b/src/set.c
@@ -12,9 +12,14 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#include "libyang.h"
 #include "common.h"
 
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "set.h"
+
 API struct ly_set *
 ly_set_new(void)
 {
@@ -30,7 +35,9 @@
 {
     unsigned int u;
 
-    LY_CHECK_ARG_RET(NULL, set,);
+    if (!set) {
+        return;
+    }
 
     if (destructor) {
         for (u = 0; u < set->count; ++u) {
@@ -43,7 +50,9 @@
 API void
 ly_set_erase(struct ly_set *set, void (*destructor)(void *obj))
 {
-    LY_CHECK_ARG_RET(NULL, set,);
+    if (!set) {
+        return;
+    }
 
     ly_set_clean(set, destructor);
 
@@ -55,7 +64,9 @@
 API void
 ly_set_free(struct ly_set *set, void (*destructor)(void *obj))
 {
-    LY_CHECK_ARG_RET(NULL, set,);
+    if (!set) {
+        return;
+    }
 
     ly_set_erase(set, destructor);
 
@@ -91,7 +102,7 @@
     new = malloc(sizeof *new);
     LY_CHECK_ERR_RET(!new, LOGMEM(NULL), NULL);
     new->count = set->count;
-    new->size = set->size;
+    new->size = set->count; /* optimize the size */
     new->objs = malloc(new->size * sizeof *(new->objs));
     LY_CHECK_ERR_RET(!new->objs, LOGMEM(NULL); free(new), NULL);
     if (duplicator) {
diff --git a/src/set.h b/src/set.h
index f16feae..9f73064 100644
--- a/src/set.h
+++ b/src/set.h
@@ -15,6 +15,8 @@
 #ifndef LY_SET_H_
 #define LY_SET_H_
 
+#include "log.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/src/tree.h b/src/tree.h
new file mode 100644
index 0000000..d700a5b
--- /dev/null
+++ b/src/tree.h
@@ -0,0 +1,158 @@
+/**
+ * @file tree.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang geenric macros and functions to work with YANG schema or data trees.
+ *
+ * Copyright (c) 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
+ */
+
+#ifndef LY_TREE_H_
+#define LY_TREE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Macro selector for other LY_ARRAY_* macros, do not use directly!
+ */
+#define LY_ARRAY_SELECT(_1, _2, NAME, ...) NAME
+
+/**
+ * @brief Helper macro to go through sized-arrays with a pointer iterator.
+ *
+ * Use with opening curly bracket (`{`).
+ *
+ * @param[in] ARRAY Array to go through
+ * @param[in] TYPE Type of the records in the ARRAY
+ * @param[out] ITER Iterating pointer to the item being processed in each loop
+ */
+#define LY_ARRAY_FOR_ITER(ARRAY, TYPE, ITER) \
+    for (ITER = ARRAY; \
+         (ARRAY) && ((void*)ITER - (void*)ARRAY)/(sizeof(TYPE)) < (*((uint32_t*)(ARRAY) - 1)); \
+         ITER = (void*)((TYPE*)ITER + 1))
+
+/**
+ * @brief Helper macro to go through sized-arrays with a numeric iterator.
+ *
+ * Use with opening curly bracket (`{`).
+ *
+ * To access an item with the INDEX value, use always LY_ARRAY_INDEX macro!
+ *
+ * @param[in] ARRAY Array to go through
+ * @param[out] INDEX Iterating index of the item being processed in each loop
+ */
+#define LY_ARRAY_FOR_INDEX(ARRAY, INDEX) \
+    for (INDEX = 0; \
+         ARRAY && INDEX < (*((uint32_t*)(ARRAY) - 1)); \
+         ++INDEX)
+
+/**
+ * @defgroup schematree Schema Tree
+ * @{
+ *
+ * Data structures and functions to manipulate and access schema tree.
+ */
+
+/**
+ * @brief Get a number of records in the ARRAY.
+ *
+ * Does not check if array exists!
+ */
+#define LY_ARRAY_SIZE(ARRAY) (*((uint32_t*)(ARRAY) - 1))
+
+/**
+ * @brief Sized-array iterator (for-loop).
+ *
+ * Use with opening curly bracket (`{`).
+ *
+ * There are 2 variants:
+ *
+ *     LY_ARRAY_FOR(ARRAY, TYPE, ITER)
+ *
+ * Where ARRAY is a sized-array to go through, TYPE is the type of the items in the ARRAY and ITER is a pointer variable
+ * providing the items of the ARRAY in the loops. This functionality is provided by LY_ARRAY_FOR_ITER macro
+ *
+ *     LY_ARRAY_FOR(ARRAY, INDEX)
+ *
+ * The ARRAY is again a sized-array to go through, the INDEX is a variable (unsigned integer) for storing iterating ARRAY's index
+ * to access the items of ARRAY in the loops. This functionality is provided by LY_ARRAY_FOR_INDEX macro.
+ */
+#define LY_ARRAY_FOR(ARRAY, ...) LY_ARRAY_SELECT(__VA_ARGS__, LY_ARRAY_FOR_ITER, LY_ARRAY_FOR_INDEX)(ARRAY, __VA_ARGS__)
+
+/**
+ * @brief Macro to iterate via all sibling elements without affecting the list itself
+ *
+ * Works for all types of nodes despite it is data or schema tree, but all the
+ * parameters must be pointers to the same type.
+ *
+ * Use with opening curly bracket (`{`). All parameters must be of the same type.
+ *
+ * @param START Pointer to the starting element.
+ * @param ELEM Iterator.
+ */
+#define LY_LIST_FOR(START, ELEM) \
+    for ((ELEM) = (START); \
+         (ELEM); \
+         (ELEM) = (ELEM)->next)
+
+/**
+ * @brief Macro to iterate via all sibling elements allowing to modify the list itself (e.g. removing elements)
+ *
+ * Use with opening curly bracket (`{`). All parameters must be of the same type.
+ *
+ * @param START Pointer to the starting element.
+ * @param NEXT Temporary storage to allow removing of the current iterator content.
+ * @param ELEM Iterator.
+ */
+#define LY_LIST_FOR_SAFE(START, NEXT, ELEM) \
+    for ((ELEM) = (START); \
+         (ELEM) ? (NEXT = (ELEM)->next, 1) : 0; \
+         (ELEM) = (NEXT))
+
+/**
+ * @brief YANG built-in types
+ */
+typedef enum
+{
+    LY_TYPE_UNKNOWN = 0, /**< Unknown type */
+    LY_TYPE_BINARY, /**< Any binary data ([RFC 6020 sec 9.8](http://tools.ietf.org/html/rfc6020#section-9.8)) */
+    LY_TYPE_UINT8, /**< 8-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_UINT16, /**< 16-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_UINT32, /**< 32-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_UINT64, /**< 64-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_STRING, /**< Human-readable string ([RFC 6020 sec 9.4](http://tools.ietf.org/html/rfc6020#section-9.4)) */
+    LY_TYPE_BITS, /**< A set of bits or flags ([RFC 6020 sec 9.7](http://tools.ietf.org/html/rfc6020#section-9.7)) */
+    LY_TYPE_BOOL, /**< "true" or "false" ([RFC 6020 sec 9.5](http://tools.ietf.org/html/rfc6020#section-9.5)) */
+    LY_TYPE_DEC64, /**< 64-bit signed decimal number ([RFC 6020 sec 9.3](http://tools.ietf.org/html/rfc6020#section-9.3))*/
+    LY_TYPE_EMPTY, /**< A leaf that does not have any value ([RFC 6020 sec 9.11](http://tools.ietf.org/html/rfc6020#section-9.11)) */
+    LY_TYPE_ENUM, /**< Enumerated strings ([RFC 6020 sec 9.6](http://tools.ietf.org/html/rfc6020#section-9.6)) */
+    LY_TYPE_IDENT, /**< A reference to an abstract identity ([RFC 6020 sec 9.10](http://tools.ietf.org/html/rfc6020#section-9.10)) */
+    LY_TYPE_INST, /**< References a data tree node ([RFC 6020 sec 9.13](http://tools.ietf.org/html/rfc6020#section-9.13)) */
+    LY_TYPE_LEAFREF, /**< A reference to a leaf instance ([RFC 6020 sec 9.9](http://tools.ietf.org/html/rfc6020#section-9.9))*/
+    LY_TYPE_UNION, /**< Choice of member types ([RFC 6020 sec 9.12](http://tools.ietf.org/html/rfc6020#section-9.12)) */
+    LY_TYPE_INT8, /**< 8-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_INT16, /**< 16-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_INT32, /**< 32-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_INT64, /**< 64-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+} LY_DATA_TYPE;
+#define LY_DATA_TYPE_COUNT 20 /**< Number of different types */
+
+/**
+ * @brief Stringified YANG built-in data types
+ */
+extern const char* ly_data_type2str[LY_DATA_TYPE_COUNT];
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_TREE_H_ */
diff --git a/src/tree_data.c b/src/tree_data.c
new file mode 100644
index 0000000..eb551e2
--- /dev/null
+++ b/src/tree_data.c
@@ -0,0 +1,253 @@
+/**
+ * @file tree_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema tree implementation
+ *
+ * Copyright (c) 2015 - 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 "common.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+
+static int
+cmp_str(const char *refstr, const char *str, size_t str_len)
+{
+
+    if (str_len) {
+        int r = strncmp(refstr, str, str_len);
+        if (!r && !refstr[str_len]) {
+            return 0;
+        } else {
+            return 1;
+        }
+    } else {
+        return strcmp(refstr, str);
+    }
+}
+
+API const struct lyd_node *
+lyd_search(const struct lyd_node *first, const struct lys_module *module,
+          const char *name, size_t name_len, uint16_t nodetype, const char *value, size_t value_len)
+{
+    const struct lyd_node *node = NULL;
+    const struct lysc_node *snode;
+
+    LY_CHECK_ARG_RET(NULL, module, name, NULL);
+    if (!nodetype) {
+        nodetype = 0xffff;
+    }
+
+    LY_LIST_FOR(first, node) {
+        snode = node->schema;
+        if (!(snode->nodetype & nodetype)) {
+            continue;
+        }
+        if (snode->module != module) {
+            continue;
+        }
+
+        if (cmp_str(snode->name, name, name_len)) {
+            continue;
+        }
+
+        if (value) {
+            if (snode->nodetype == LYS_LIST) {
+                /* TODO handle value as keys of the list instance */
+            } else if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+                if (cmp_str(((struct lyd_node_term*)node)->value.canonized, value, value_len)) {
+                    continue;
+                }
+            } else {
+                continue;
+            }
+        }
+
+        /* all criteria passed */
+        return node;
+    }
+    return NULL;
+}
+
+static struct lyd_node *
+lyd_parse_mem_(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, va_list ap)
+{
+    struct lyd_node *result = NULL;
+    const struct lyd_node *rpc_act = NULL, *data_tree = NULL, *iter;
+#if 0
+    const char *yang_data_name = NULL;
+#endif
+
+    if (lyd_parse_check_options(ctx, options, __func__)) {
+        return NULL;
+    }
+
+    if (options & LYD_OPT_RPCREPLY) {
+        rpc_act = va_arg(ap, const struct lyd_node *);
+        if (!rpc_act || rpc_act->parent || !(rpc_act->schema->nodetype & (LYS_ACTION | LYS_LIST | LYS_CONTAINER))) {
+            LOGERR(ctx, LY_EINVAL, "Data parser invalid variable parameter (const struct lyd_node *rpc_act).");
+            return NULL;
+        }
+    }
+    if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF | LYD_OPT_RPCREPLY)) {
+        data_tree = va_arg(ap, const struct lyd_node *);
+        if (data_tree) {
+            if (options & LYD_OPT_NOEXTDEPS) {
+                LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree and LYD_OPT_NOEXTDEPS set).",
+                       __func__);
+                return NULL;
+            }
+
+            LY_LIST_FOR(data_tree, iter) {
+                if (iter->parent) {
+                    /* a sibling is not top-level */
+                    LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *data_tree).", __func__);
+                    return NULL;
+                }
+            }
+
+            /* move it to the beginning */
+            for (; data_tree->prev->next; data_tree = data_tree->prev);
+
+            /* LYD_OPT_NOSIBLINGS cannot be set in this case */
+            if (options & LYD_OPT_NOSIBLINGS) {
+                LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree with LYD_OPT_NOSIBLINGS).", __func__);
+                return NULL;
+            }
+        }
+    }
+#if 0
+    if (options & LYD_OPT_DATA_TEMPLATE) {
+        yang_data_name = va_arg(ap, const char *);
+    }
+#endif
+
+    if (!format) {
+        /* TODO try to detect format from the content */
+    }
+
+    switch (format) {
+    case LYD_XML:
+        lyd_parse_xml(ctx, data, options, &result);
+        break;
+#if 0
+    case LYD_JSON:
+        lyd_parse_json(ctx, data, options, rpc_act, data_tree, yang_data_name);
+        break;
+    case LYD_LYB:
+        lyd_parse_lyb(ctx, data, options, data_tree, yang_data_name, NULL);
+        break;
+#endif
+    case LYD_UNKNOWN:
+        LOGINT(ctx);
+        break;
+    }
+
+    return result;
+}
+
+API struct lyd_node *
+lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, ...)
+{
+    va_list ap;
+    struct lyd_node *result;
+
+    va_start(ap, options);
+    result = lyd_parse_mem_(ctx, data, format, options, ap);
+    va_end(ap);
+
+    return result;
+}
+
+static struct lyd_node *
+lyd_parse_fd_(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, va_list ap)
+{
+    struct lyd_node *result;
+    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);
+    result = lyd_parse_mem_(ctx, addr ? addr : "", format, options, ap);
+    if (addr) {
+        ly_munmap(addr, length);
+    }
+
+    return result;
+}
+
+API struct lyd_node *
+lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, ...)
+{
+    struct lyd_node *ret;
+    va_list ap;
+
+    va_start(ap, options);
+    ret = lyd_parse_fd_(ctx, fd, format, options, ap);
+    va_end(ap);
+
+    return ret;
+}
+
+API struct lyd_node *
+lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, ...)
+{
+    int fd;
+    struct lyd_node *result;
+    size_t len;
+    va_list ap;
+
+    LY_CHECK_ARG_RET(ctx, ctx, path, 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);
+
+    if (!format) {
+        /* unknown format - try to detect it from filename's suffix */
+        len = strlen(path);
+
+        /* ignore trailing whitespaces */
+        for (; len > 0 && isspace(path[len - 1]); len--);
+
+        if (len >= 5 && !strncmp(&path[len - 4], ".xml", 4)) {
+            format = LYD_XML;
+#if 0
+        } else if (len >= 6 && !strncmp(&path[len - 5], ".json", 5)) {
+            format = LYD_JSON;
+        } else if (len >= 5 && !strncmp(&path[len - 4], ".lyb", 4)) {
+            format = LYD_LYB;
+#endif
+        } /* else still unknown, try later to detect it from the content */
+    }
+
+    va_start(ap, options);
+    result = lyd_parse_fd_(ctx, fd, format, options, ap);
+
+    va_end(ap);
+    close(fd);
+
+    return result;
+}
diff --git a/src/tree_data.h b/src/tree_data.h
new file mode 100644
index 0000000..0e1626a
--- /dev/null
+++ b/src/tree_data.h
@@ -0,0 +1,571 @@
+/**
+ * @file tree_data.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang representation of YANG data trees.
+ *
+ * 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
+ */
+
+#ifndef LY_TREE_DATA_H_
+#define LY_TREE_DATA_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "log.h"
+#include "tree.h"
+#include "tree_schema.h"
+
+struct ly_ctx;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup datatree Data Tree
+ * @{
+ *
+ * Data structures and functions to manipulate and access instance data tree.
+ */
+
+/**
+ * @brief Macro to iterate via all elements in a data tree. This is the opening part
+ * to the #LYD_TREE_DFS_END - they always have to be used together.
+ *
+ * The function follows deep-first search algorithm:
+ * <pre>
+ *     1
+ *    / \
+     *   2   4
+ *  /   / \
+     * 3   5   6
+ * </pre>
+ *
+ * Use the same parameters for #LY_TREE_DFS_BEGIN and #LY_TREE_DFS_END. While
+ * START can be any of the lyd_node* types, NEXT and ELEM variables are expected
+ * to be pointers to a generic struct lyd_node.
+ *
+ * Since the next node is selected as part of #LYD_TREE_DFS_END, do not use
+ * continue statement between the #LYD_TREE_DFS_BEGIN and #LYD_TREE_DFS_END.
+ *
+ * Use with opening curly bracket '{' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param NEXT Temporary storage, do not use.
+ * @param ELEM Iterator intended for use in the block.
+ */
+#define LYD_TREE_DFS_BEGIN(START, NEXT, ELEM) \
+    for ((ELEM) = (NEXT) = (START); \
+         (ELEM); \
+         (ELEM) = (NEXT))
+
+/**
+ * @brief Macro to iterate via all elements in a tree. This is the closing part
+ * to the #LYD_TREE_DFS_BEGIN - they always have to be used together.
+ *
+ * Use the same parameters for #LYD_TREE_DFS_BEGIN and #LYD_TREE_DFS_END. While
+ * START can be any of the lyd_node* types, NEXT and ELEM variables are expected
+ * to be pointers to a generic struct lyd_node.
+ *
+ * Use with closing curly bracket '}' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param NEXT Temporary storage, do not use.
+ * @param ELEM Iterator intended for use in the block.
+ */
+
+#define LYD_TREE_DFS_END(START, NEXT, ELEM) \
+    /* select element for the next run - children first */ \
+    (NEXT) = (struct lyd_node*)lyd_node_children((struct lyd_node*)ELEM); \
+    if (!(NEXT)) { \
+        /* no children */ \
+        if ((ELEM) == (struct lyd_node*)(START)) { \
+            /* we are done, (START) has no children */ \
+            break; \
+        } \
+        /* try siblings */ \
+        (NEXT) = (ELEM)->next; \
+    } \
+    while (!(NEXT)) { \
+        /* parent is already processed, go to its sibling */ \
+        (ELEM) = (struct lyd_node*)(ELEM)->parent; \
+        /* no siblings, go back through parents */ \
+        if ((ELEM)->parent == (START)->parent) { \
+            /* we are done, no next element to process */ \
+            break; \
+        } \
+        (NEXT) = (ELEM)->next; \
+    }
+
+/**
+ * @brief Data input/output formats supported by libyang [parser](@ref howtodataparsers) and
+ * [printer](@ref howtodataprinters) functions.
+ */
+typedef enum {
+    LYD_UNKNOWN = 0,     /**< unknown format, used as return value in case of error */
+    LYD_XML,             /**< XML format of the instance data */
+#if 0
+    LYD_JSON,            /**< JSON format of the instance data */
+    LYD_LYB,             /**< LYB format of the instance data */
+#endif
+} LYD_FORMAT;
+
+/**
+ * @brief List of possible value types stored in ::lyd_node_anydata.
+ */
+typedef enum {
+    LYD_ANYDATA_CONSTSTRING = 0x00, /**< value is constant string (const char *) which is internally duplicated for
+                                         storing in the anydata structure; XML sensitive characters (such as & or \>)
+                                         are automatically escaped when the anydata is printed in XML format. */
+    LYD_ANYDATA_STRING = 0x01,      /**< value is dynamically allocated string (char*), so the data are used directly
+                                         without duplication and caller is supposed to not manipulate with the data
+                                         after a successful call (including calling free() on the provided data); XML
+                                         sensitive characters (such as & or \>) are automatically escaped when the
+                                         anydata is printed in XML format */
+    LYD_ANYDATA_JSON = 0x02,        /**< value is string containing the data modeled by YANG and encoded as I-JSON. The
+                                         string is handled as constant string. In case of using the value as input
+                                         parameter, the #LYD_ANYDATA_JSOND can be used for dynamically allocated
+                                         string. */
+    LYD_ANYDATA_JSOND = 0x03,       /**< In case of using value as input parameter, this enumeration is supposed to be
+                                         used for dynamically allocated strings (it is actually combination of
+                                         #LYD_ANYDATA_JSON and #LYD_ANYDATA_STRING (and it can be also specified as
+                                         ORed value of the mentioned values. */
+    LYD_ANYDATA_SXML = 0x04,        /**< value is string containing the serialized XML data. The string is handled as
+                                         constant string. In case of using the value as input parameter, the
+                                         #LYD_ANYDATA_SXMLD can be used for dynamically allocated string. */
+    LYD_ANYDATA_SXMLD = 0x05,       /**< In case of using serialized XML value as input parameter, this enumeration is
+                                         supposed to be used for dynamically allocated strings (it is actually
+                                         combination of #LYD_ANYDATA_SXML and #LYD_ANYDATA_STRING (and it can be also
+                                         specified as ORed value of the mentioned values). */
+    LYD_ANYDATA_XML = 0x08,         /**< value is struct lyxml_elem*, the structure is directly connected into the
+                                         anydata node without duplication, caller is supposed to not manipulate with the
+                                         data after a successful call (including calling lyxml_free() on the provided
+                                         data) */
+    LYD_ANYDATA_DATATREE = 0x10,    /**< value is struct lyd_node* (first sibling), the structure is directly connected
+                                         into the anydata node without duplication, caller is supposed to not manipulate
+                                         with the data after a successful call (including calling lyd_free() on the
+                                         provided data) */
+    LYD_ANYDATA_LYB = 0x20,         /**< value is a memory with serialized data tree in LYB format. The data are handled
+                                         as a constant string. In case of using the value as input parameter,
+                                         the #LYD_ANYDATA_LYBD can be used for dynamically allocated string. */
+    LYD_ANYDATA_LYBD = 0x21,        /**< In case of using LYB value as input parameter, this enumeration is
+                                         supposed to be used for dynamically allocated strings (it is actually
+                                         combination of #LYD_ANYDATA_LYB and #LYD_ANYDATA_STRING (and it can be also
+                                         specified as ORed value of the mentioned values). */
+} LYD_ANYDATA_VALUETYPE;
+
+/** @} */
+
+/**
+ * @brief YANG data representation
+ */
+struct lyd_value {
+    const char *canonized;          /**< string representation of value (for comparison, printing,...), canonized according to the
+                                          rules implemented in the type's canonization callback (if any). */
+    union {
+        const char *string;         /**< original, non-canonized string value. Useful for example for unions where the type (and therefore
+                                         the cannonization rules) can change by changing value (e.g. leafref target) somewhere else. */
+        int8_t boolean;              /**< 0 as false, 1 as true */
+        int64_t dec64;               /**< decimal64: value = dec64 / 10^fraction-digits  */
+        struct lysc_type_bitenum_item *enum_item;  /**< pointer to the definition of the enumeration value */
+        struct lysc_type_bitenum_item **bits_items; /**< list of set pointers to the specification of the set bits ([sized array](@ref sizedarrays)) */
+        struct lysc_ident *ident;    /**< pointer to the schema definition of the identityref value */
+        int8_t int8;                 /**< 8-bit signed integer */
+        int16_t int16;               /**< 16-bit signed integer */
+        int32_t int32;               /**< 32-bit signed integer */
+        int64_t int64;               /**< 64-bit signed integer */
+        uint8_t uint8;               /**< 8-bit unsigned integer */
+        uint16_t uint16;             /**< 16-bit signed integer */
+        uint32_t uint32;             /**< 32-bit signed integer */
+        uint64_t uint64;             /**< 64-bit signed integer */
+        void *ptr;                   /**< generic data type structure used to store the data */
+    };  /**< The union is just a list of shorthands to possible values stored by a type's plugin. libyang works only with the canonized string,
+             this specific data type storage is just to simplify use of the values by the libyang users. */
+};
+
+/**
+ * @brief Attribute structure.
+ *
+ * The structure provides information about attributes of a data element. Such attributes must map to
+ * annotations as specified in RFC 7952. The only exception is the filter type (in NETCONF get operations)
+ * and edit-config's operation attributes. In XML, they are represented as standard XML attributes. In JSON,
+ * they are represented as JSON elements starting with the '@' character (for more information, see the
+ * YANG metadata RFC.
+ *
+ */
+struct lyd_attr {
+    struct lyd_node *parent;         /**< data node where the attribute is placed */
+    struct lyd_attr *next;           /**< pointer to the next attribute of the same element */
+    void *annotation;                /**< TODO pointer to the attribute/annotation's definition */
+    const char *name;                /**< attribute name */
+    struct lyd_value value;            /**< attribute's value representation */
+};
+
+
+#define LYD_NODE_INNER (LYS_CONTAINER|LYS_LIST) /**< Schema nodetype mask for lyd_node_inner */
+#define LYD_NODE_TERM (LYS_LEAF|LYS_LEAFLIST)   /**< Schema nodetype mask for lyd_node_term */
+#define LYD_NODE_ANY (LYS_ANYDATA)   /**< Schema nodetype mask for lyd_node_any */
+
+/**
+ * @brief Generic structure for a data node.
+ */
+struct lyd_node {
+    uint32_t hash;                   /**< hash of this particular node (module name + schema name + key string values if list) */
+    const struct lysc_node *schema;  /**< pointer to the schema definition of this node */
+    struct lyd_node_inner *parent;   /**< pointer to the parent node, NULL in case of root node */
+    struct lyd_node *next;           /**< pointer to the next sibling node (NULL if there is no one) */
+    struct lyd_node *prev;           /**< pointer to the previous sibling node \note Note that this pointer is
+                                          never NULL. If there is no sibling node, pointer points to the node
+                                          itself. In case of the first node, this pointer points to the last
+                                          node in the list. */
+    struct lyd_attr *attr;           /**< pointer to the list of attributes of this node */
+
+#ifdef LY_ENABLED_LYD_PRIV
+    void *priv;                      /**< private user data, not used by libyang */
+#endif
+};
+
+/**
+ * @brief Data node structure for the inner data tree nodes - containers and lists.
+ */
+struct lyd_node_inner {
+    uint32_t hash;                   /**< hash of this particular node (module name + schema name + key string values if list) */
+    const struct lysc_node *schema;  /**< pointer to the schema definition of this node */
+    struct lyd_node_inner *parent;   /**< pointer to the parent node, NULL in case of root node */
+    struct lyd_node *next;           /**< pointer to the next sibling node (NULL if there is no one) */
+    struct lyd_node *prev;           /**< pointer to the previous sibling node \note Note that this pointer is
+                                          never NULL. If there is no sibling node, pointer points to the node
+                                          itself. In case of the first node, this pointer points to the last
+                                          node in the list. */
+    struct lyd_attr *attr;           /**< pointer to the list of attributes of this node */
+
+#ifdef LY_ENABLED_LYD_PRIV
+    void *priv;                      /**< private user data, not used by libyang */
+#endif
+
+    struct lyd_node *child;          /**< pointer to the first child node. */
+    struct hash_table *children_ht;  /**< hash table with all the direct children (except keys for a list, lists without keys) */
+};
+
+/**
+ * @brief Data node structure for the terminal data tree nodes - leafs and leaf-lists.
+ */
+struct lyd_node_term {
+    uint32_t hash;                   /**< hash of this particular node (module name + schema name + key string values if list) */
+    const struct lysc_node *schema;  /**< pointer to the schema definition of this node */
+    struct lyd_node_inner *parent;   /**< pointer to the parent node, NULL in case of root node */
+    struct lyd_node *next;           /**< pointer to the next sibling node (NULL if there is no one) */
+    struct lyd_node *prev;           /**< pointer to the previous sibling node \note Note that this pointer is
+                                          never NULL. If there is no sibling node, pointer points to the node
+                                          itself. In case of the first node, this pointer points to the last
+                                          node in the list. */
+    struct lyd_attr *attr;           /**< pointer to the list of attributes of this node */
+
+#ifdef LY_ENABLED_LYD_PRIV
+    void *priv;                      /**< private user data, not used by libyang */
+#endif
+
+    struct lyd_value value;            /**< node's value representation */
+};
+
+/**
+ * @brief Data node structure for the anydata data tree nodes - anydatas and anyxmls.
+ */
+struct lyd_node_any {
+    uint32_t hash;                   /**< hash of this particular node (module name + schema name + key string values if list) */
+    const struct lysc_node *schema;  /**< pointer to the schema definition of this node */
+    struct lyd_node_inner *parent;   /**< pointer to the parent node, NULL in case of root node */
+    struct lyd_node *next;           /**< pointer to the next sibling node (NULL if there is no one) */
+    struct lyd_node *prev;           /**< pointer to the previous sibling node \note Note that this pointer is
+                                          never NULL. If there is no sibling node, pointer points to the node
+                                          itself. In case of the first node, this pointer points to the last
+                                          node in the list. */
+    struct lyd_attr *attr;           /**< pointer to the list of attributes of this node */
+
+#ifdef LY_ENABLED_LYD_PRIV
+    void *priv;                      /**< private user data, not used by libyang */
+#endif
+
+    /* TODO - anydata representation */
+};
+
+/**
+ * @defgroup dataparseroptions Data parser options
+ * @ingroup datatree
+ *
+ * Various options to change the data tree parsers behavior.
+ *
+ * Default behavior:
+ * - in case of XML, parser reads all data from its input (file, memory, XML tree) including the case of not well-formed
+ * XML document (multiple top-level elements) and if there is an unknown element, it is skipped including its subtree
+ * (see the next point). This can be changed by the #LYD_OPT_NOSIBLINGS option which make parser to read only a single
+ * tree (with a single root element) from its input.
+ * - parser silently ignores the data without a matching node in schema trees. If the caller want to stop
+ * parsing in case of presence of unknown data, the #LYD_OPT_STRICT can be used. The strict mode is useful for
+ * NETCONF servers, since NETCONF clients should always send data according to the capabilities announced by the server.
+ * On the other hand, the default non-strict mode is useful for clients receiving data from NETCONF server since
+ * clients are not required to understand everything the server does. Of course, the optimal strategy for clients is
+ * to use filtering to get only the required data. Having an unknown element of the known namespace is always an error.
+ * The behavior can be changed by #LYD_OPT_STRICT option.
+ * - using obsolete statements (status set to obsolete) just generates a warning, but the processing continues. The
+ * behavior can be changed by #LYD_OPT_OBSOLETE option.
+ * - parser expects that the provided data provides complete datastore content (both the configuration and state data)
+ * and performs data validation according to all YANG rules. This can be a problem in case of representing NETCONF's
+ * subtree filter data, edit-config's data or other type of data set - such data do not represent a complete data set
+ * and some of the validation rules can fail. Therefore there are other options (within lower 8 bits) to make parser
+ * to accept such a data.
+ * - when parser evaluates when-stmt condition to false, a validation error is raised. If the
+ * #LYD_OPT_WHENAUTODEL is used, the invalid node is silently removed instead of an error. The option (and also this default
+ * behavior) takes effect only in case of #LYD_OPT_DATA or #LYD_OPT_CONFIG type of data.
+ * @{
+ */
+
+#define LYD_OPT_DATA       0x00 /**< Default type of data - complete datastore content with configuration as well as
+                                     state data. To handle possibly missing (but by default required) ietf-yang-library
+                                     data, use #LYD_OPT_DATA_NO_YANGLIB or #LYD_OPT_DATA_ADD_YANGLIB options. */
+#define LYD_OPT_CONFIG     0x01 /**< A configuration datastore - complete datastore without state data.
+                                     Validation modifications:
+                                     - status data are not allowed */
+#define LYD_OPT_GET        0x02 /**< Data content from a NETCONF reply message to the NETCONF \<get\> operation.
+                                     Validation modifications:
+                                     - mandatory nodes can be omitted
+                                     - leafrefs and instance-identifier resolution is allowed to fail
+                                     - list's keys/unique nodes are not required (so duplication is not checked)
+                                     - must and when evaluation skipped */
+#define LYD_OPT_GETCONFIG  0x04 /**< Data content from a NETCONF reply message to the NETCONF \<get-config\> operation
+                                     Validation modifications:
+                                     - mandatory nodes can be omitted
+                                     - leafrefs and instance-identifier resolution is allowed to fail
+                                     - list's keys/unique nodes are not required (so duplication is not checked)
+                                     - must and when evaluation skipped
+                                     - status data are not allowed */
+#define LYD_OPT_EDIT       0x08 /**< Content of the NETCONF \<edit-config\>'s config element.
+                                     Validation modifications:
+                                     - mandatory nodes can be omitted
+                                     - leafrefs and instance-identifier resolution is allowed to fail
+                                     - must and when evaluation skipped
+                                     - status data are not allowed */
+#define LYD_OPT_RPC        0x10 /**< Data represents RPC or action input parameters. */
+#define LYD_OPT_RPCREPLY   0x20 /**< Data represents RPC or action output parameters (maps to NETCONF <rpc-reply> data). */
+#define LYD_OPT_NOTIF      0x40 /**< Data represents an event notification data. */
+#define LYD_OPT_NOTIF_FILTER 0x80 /**< Data represents a filtered event notification data.
+                                       Validation modification:
+                                       - the only requirement is that the data tree matches the schema tree */
+#define LYD_OPT_TYPEMASK   0x10000ff /**< Mask to filter data type options. Always only a single data type option (only
+                                          single bit from the lower 8 bits) can be set. */
+
+/* 0x100 reserved, used internally */
+#define LYD_OPT_STRICT     0x0200 /**< Instead of silent ignoring data without schema definition, raise an error. */
+#define LYD_OPT_DESTRUCT   0x0400 /**< Free the provided XML tree during parsing the data. With this option, the
+                                       provided XML tree is affected and all successfully parsed data are freed.
+                                       This option is applicable only to lyd_parse_xml() function. */
+#define LYD_OPT_OBSOLETE   0x0800 /**< Raise an error when an obsolete statement (status set to obsolete) is used. */
+#define LYD_OPT_NOSIBLINGS 0x1000 /**< Parse only a single XML tree from the input. This option applies only to
+                                       XML input data. */
+#define LYD_OPT_TRUSTED    0x2000 /**< Data comes from a trusted source and it is not needed to validate them. Data
+                                       are connected with the schema, but the most validation checks (mandatory nodes,
+                                       list instance uniqueness, etc.) are not performed. This option does not make
+                                       sense for lyd_validate() so it is ignored by this function. */
+#define LYD_OPT_WHENAUTODEL 0x4000 /**< Automatically delete subtrees with false when-stmt condition. The flag is
+                                        applicable only in combination with #LYD_OPT_DATA and #LYD_OPT_CONFIG flags.
+                                        If used, libyang will not generate a validation error. */
+#define LYD_OPT_NOEXTDEPS  0x8000 /**< Allow external dependencies (external leafrefs, instance-identifiers, must,
+                                       and when) to not be resolved/satisfied during validation. */
+#define LYD_OPT_DATA_NO_YANGLIB  0x10000 /**< Ignore (possibly) missing ietf-yang-library data. Applicable only with #LYD_OPT_DATA. */
+#define LYD_OPT_DATA_ADD_YANGLIB 0x20000 /**< Add missing ietf-yang-library data into the validated data tree. Applicable
+                                              only with #LYD_OPT_DATA. If some ietf-yang-library data are present, they are
+                                              preserved and option is ignored. */
+#define LYD_OPT_VAL_DIFF 0x40000 /**< Flag only for validation, store all the data node changes performed by the validation
+                                      in a diff structure. */
+#define LYD_OPT_DATA_TEMPLATE 0x1000000 /**< Data represents YANG data template. */
+
+/**@} dataparseroptions */
+
+/**
+ * @brief Get the node's children list if any.
+ *
+ * Decides the node's type and in case it has a children list, returns it.
+ * @param[in] node Node to check.
+ * @return Pointer to the first child node (if any) of the \p node.
+ */
+const struct lyd_node *lyd_node_children(const struct lyd_node *node);
+
+/**
+ * @brief Find the node, in the list, satisfying the given restrictions.
+ *
+ * @param[in] first Starting child node for search.
+ * @param[in] module Module of the node to find (mandatory argument).
+ * @param[in] name Name of the node to find (mandatory argument).
+ * @param[in] name_len Optional length of the @p name argument in case it is not NULL-terminated string.
+ * @param[in] nodetype Optional mask for the nodetype of the node to find, 0 is understood as all nodetypes.
+ * @param[in] value Optional restriction for lyd_node_term nodes to select node with the specific value.
+ * @param[in] value_len Optional length of the @p value argument in case it is not NULL-terminated string.
+ * @return The sibling node of the @p first (or itself), satisfying the given restrictions.
+ * @return NULL in case there is no node satisfying the restrictions.
+ */
+const struct lyd_node *lyd_search(const struct lyd_node *first, const struct lys_module *module,
+                                  const char *name, size_t name_len, uint16_t nodetype, const char *value, size_t value_len);
+
+/**
+ * @brief Parse (and validate) data from memory.
+ *
+ * In case of LY_XML format, the data string is parsed completely. It means that when it contains
+ * a non well-formed XML with multiple root elements, all those sibling XML trees are parsed. The
+ * returned data node is a root of the first tree with other trees connected via the next pointer.
+ * This behavior can be changed by #LYD_OPT_NOSIBLINGS option.
+ *
+ * @param[in] ctx Context to connect with the data tree being built here.
+ * @param[in] data Serialized data in the specified format.
+ * @param[in] format Format of the input data to be parsed.
+ * @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
+ * @param[in] ... Variable arguments depend on \p options. If they include:
+ *                - #LYD_OPT_DATA:
+ *                - #LYD_OPT_CONFIG:
+ *                - #LYD_OPT_GET:
+ *                - #LYD_OPT_GETCONFIG:
+ *                - #LYD_OPT_EDIT:
+ *                  - no variable arguments expected.
+ *                - #LYD_OPT_RPC:
+ *                - #LYD_OPT_NOTIF:
+ *                  - struct lyd_node *data_tree - additional data tree that will be used
+ *                    when checking any "when" or "must" conditions in the parsed tree that require
+ *                    some nodes outside their subtree. It must be a list of top-level elements!
+ *                - #LYD_OPT_RPCREPLY:
+ *                  - const struct ::lyd_node *rpc_act - pointer to the whole RPC or (top-level) action operation
+ *                    data tree (the request) of the reply.
+ *                  - const struct ::lyd_node *data_tree - additional data tree that will be used
+ *                    when checking any "when" or "must" conditions in the parsed tree that require
+ *                    some nodes outside their subtree. It must be a list of top-level elements!
+ * @return Pointer to the built data tree or NULL in case of empty \p data. To free the returned structure,
+ *         use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
+ *         #ly_errno contains appropriate error code (see #LY_ERR).
+ */
+struct lyd_node *lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, ...);
+
+/**
+ * @brief Read (and validate) data from the given file descriptor.
+ *
+ * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
+ *
+ * In case of LY_XML format, the file content is parsed completely. It means that when it contains
+ * a non well-formed XML with multiple root elements, all those sibling XML trees are parsed. The
+ * returned data node is a root of the first tree with other trees connected via the next pointer.
+ * This behavior can be changed by #LYD_OPT_NOSIBLINGS option.
+ *
+ * @param[in] ctx Context to connect with the data tree being built here.
+ * @param[in] fd The standard file descriptor of the file containing the data tree in the specified format.
+ * @param[in] format Format of the input data to be parsed.
+ * @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
+ * @param[in] ... Variable arguments depend on \p options. If they include:
+ *                - #LYD_OPT_DATA:
+ *                - #LYD_OPT_CONFIG:
+ *                - #LYD_OPT_GET:
+ *                - #LYD_OPT_GETCONFIG:
+ *                - #LYD_OPT_EDIT:
+ *                  - no variable arguments expected.
+ *                - #LYD_OPT_RPC:
+ *                - #LYD_OPT_NOTIF:
+ *                  - struct lyd_node *data_tree - additional data tree that will be used
+ *                    when checking any "when" or "must" conditions in the parsed tree that require
+ *                    some nodes outside their subtree. It must be a list of top-level elements!
+ *                - #LYD_OPT_RPCREPLY:
+ *                  - const struct ::lyd_node *rpc_act - pointer to the whole RPC or action operation data
+ *                    tree (the request) of the reply.
+ *                  - const struct ::lyd_node *data_tree - additional data tree that will be used
+ *                    when checking any "when" or "must" conditions in the parsed tree that require
+ *                    some nodes outside their subtree. It must be a list of top-level elements!
+ * @return Pointer to the built data tree or NULL in case of empty file. To free the returned structure,
+ *         use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
+ *         #ly_errno contains appropriate error code (see #LY_ERR).
+ */
+struct lyd_node *lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, ...);
+
+/**
+ * @brief Read (and validate) data from the given file path.
+ *
+ * In case of LY_XML format, the file content is parsed completely. It means that when it contains
+ * a non well-formed XML with multiple root elements, all those sibling XML trees are parsed. The
+ * returned data node is a root of the first tree with other trees connected via the next pointer.
+ * This behavior can be changed by #LYD_OPT_NOSIBLINGS option.
+ *
+ * @param[in] ctx Context to connect with the data tree being built here.
+ * @param[in] path Path to the file containing the data tree in the specified format.
+ * @param[in] format Format of the input data to be parsed.
+ * @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
+ * @param[in] ... Variable arguments depend on \p options. If they include:
+ *                - #LYD_OPT_DATA:
+ *                - #LYD_OPT_CONFIG:
+ *                - #LYD_OPT_GET:
+ *                - #LYD_OPT_GETCONFIG:
+ *                - #LYD_OPT_EDIT:
+ *                  - no variable arguments expected.
+ *                - #LYD_OPT_RPC:
+ *                - #LYD_OPT_NOTIF:
+ *                  - struct lyd_node *data_tree - additional data tree that will be used
+ *                    when checking any "when" or "must" conditions in the parsed tree that require
+ *                    some nodes outside their subtree. It must be a list of top-level elements!
+ *                - #LYD_OPT_RPCREPLY:
+ *                  - const struct ::lyd_node *rpc_act - pointer to the whole RPC or action operation data
+ *                    tree (the request) of the reply.
+ *                  - const struct ::lyd_node *data_tree - additional data tree that will be used
+ *                    when checking any "when" or "must" conditions in the parsed tree that require
+ *                    some nodes outside their subtree. It must be a list of top-level elements!
+ * @return Pointer to the built data tree or NULL in case of empty file. To free the returned structure,
+ *         use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
+ *         #ly_errno contains appropriate error code (see #LY_ERR).
+ */
+struct lyd_node *lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, ...);
+
+/**
+ * @brief Free all the nodes in the data tree.
+ *
+ * @param[in] node Any of the nodes inside the tree.
+ */
+void lyd_free_all(struct lyd_node *node);
+
+/**
+ * @brief Free (and unlink) the specified data (sub)tree.
+ *
+ * __PARTIAL CHANGE__ - validate after the final change on the data tree (see @ref howtodatamanipulators).
+ *
+ * @param[in] node Root of the (sub)tree to be freed.
+ */
+void lyd_free_tree(struct lyd_node *node);
+
+/**
+ * @brief Unlink the specified data subtree. All referenced namespaces are copied.
+ *
+ * Note, that the node's connection with the schema tree is kept. Therefore, in case of
+ * reconnecting the node to a data tree using lyd_paste() it is necessary to paste it
+ * to the appropriate place in the data tree following the schema.
+ *
+ * __PARTIAL CHANGE__ - validate after the final change on the data tree (see @ref howtodatamanipulators).
+ *
+ * @param[in] node Data tree node to be unlinked (together with all children).
+ * @return LY_SUCCESS for success
+ * @return LY_E* values in case of error
+ */
+LY_ERR lyd_unlink_tree(struct lyd_node *node);
+
+/**
+ * @brief Destroy data attribute.
+ *
+ * @param[in] ctx Context where the attribute was created.
+ * @param[in] attr Attribute to destroy
+ * @param[in] recursive Zero to destroy only the attribute (the attribute list is corrected),
+ * non-zero to destroy also all the subsequent attributes in the list.
+ */
+void lyd_free_attr(struct ly_ctx *ctx, struct lyd_attr *attr, int recursive);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_TREE_DATA_H_ */
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
new file mode 100644
index 0000000..f594872
--- /dev/null
+++ b/src/tree_data_free.c
@@ -0,0 +1,230 @@
+/**
+ * @file tree_data_free.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Freeing functions for data tree structures
+ *
+ * Copyright (c) 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
+ */
+
+#include "common.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "hash_table.h"
+#include "log.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+#include "tree_data_internal.h"
+#include "plugins_types.h"
+
+API LY_ERR
+lyd_unlink_tree(struct lyd_node *node)
+{
+    struct lyd_node *iter;
+    struct lyd_node **first_sibling;
+
+    LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
+
+    if (node->parent) {
+        first_sibling = lyd_node_children_p((struct lyd_node*)node->parent);
+    }
+
+    /* unlink from siblings */
+    if (node->prev->next) {
+        node->prev->next = node->next;
+    }
+    if (node->next) {
+        node->next->prev = node->prev;
+    } else {
+        /* unlinking the last node */
+        if (node->parent) {
+            iter = *first_sibling;
+        } else {
+            iter = node->prev;
+            while (iter->prev != node) {
+                iter = iter->prev;
+            }
+        }
+        /* update the "last" pointer from the first node */
+        iter->prev = node->prev;
+    }
+
+    /* unlink from parent */
+    if (node->parent) {
+        if (*first_sibling == node) {
+            /* the node is the first child */
+            *first_sibling = node->next;
+        }
+        lyd_unlink_hash(node);
+        node->parent = NULL;
+    }
+
+    node->next = NULL;
+    node->prev = node;
+
+    return EXIT_SUCCESS;
+}
+
+/**
+ * @brief Free the YANG data value content.
+ * @param[in] ctx libyang context
+ * @param[in] value Data value structure to clean - since it is mostly part of other structure, only content is freed.
+ */
+static void
+lyd_free_value(struct ly_ctx *ctx, struct lyd_value *value, struct lysc_type *type)
+{
+    if (type->plugin->free) {
+        type->plugin->free(ctx, type, value);
+    }
+
+    FREE_STRING(ctx, value->canonized);
+
+}
+
+API void
+lyd_free_attr(struct ly_ctx *ctx, struct lyd_attr *attr, int recursive)
+{
+    struct lyd_attr *iter;
+
+    LY_CHECK_ARG_RET(NULL, ctx, );
+    if (!attr) {
+        return;
+    }
+
+    if (attr->parent) {
+        if (attr->parent->attr == attr) {
+            if (recursive) {
+                attr->parent->attr = NULL;
+            } else {
+                attr->parent->attr = attr->next;
+            }
+        } else {
+            for (iter = attr->parent->attr; iter->next != attr; iter = iter->next);
+            if (iter->next) {
+                if (recursive) {
+                    iter->next = NULL;
+                } else {
+                    iter->next = attr->next;
+                }
+            }
+        }
+    }
+
+    if (!recursive) {
+        attr->next = NULL;
+    }
+
+    for(iter = attr; iter; ) {
+        attr = iter;
+        iter = iter->next;
+
+        FREE_STRING(ctx, attr->name);
+        lyd_free_value(ctx, &attr->value, NULL /* TODO */);
+        free(attr);
+    }
+}
+
+/**
+ * @brief Free Data (sub)tree.
+ * @param[in] ctx libyang context.
+ * @param[in] node Data node to be freed.
+ * @param[in] top Recursion flag to unlink the root of the subtree being freed.
+ */
+static void
+lyd_free_subtree(struct ly_ctx *ctx, struct lyd_node *node, int top)
+{
+    struct lyd_node *iter, *next;
+    struct lyd_node *children;
+
+    assert(node);
+
+    /* remove children hash table in case of inner data node */
+    if (node->schema->nodetype & LYD_NODE_INNER) {
+        lyht_free(((struct lyd_node_inner*)node)->children_ht);
+        ((struct lyd_node_inner*)node)->children_ht = NULL;
+
+        /* free the children */
+        children = (struct lyd_node*)lyd_node_children(node);
+        LY_LIST_FOR_SAFE(children, next, iter) {
+            lyd_free_subtree(ctx, iter, 0);
+        }
+    } else if (node->schema->nodetype & LYD_NODE_ANY) {
+        /* TODO anydata */
+#if 0
+        switch (((struct lyd_node_anydata *)node)->value_type) {
+        case LYD_ANYDATA_CONSTSTRING:
+        case LYD_ANYDATA_SXML:
+        case LYD_ANYDATA_JSON:
+            FREE_STRING(node->schema->module->ctx, ((struct lyd_node_anydata *)node)->value.str);
+            break;
+        case LYD_ANYDATA_DATATREE:
+            lyd_free_withsiblings(((struct lyd_node_anydata *)node)->value.tree);
+            break;
+        case LYD_ANYDATA_XML:
+            lyxml_free_withsiblings(node->schema->module->ctx, ((struct lyd_node_anydata *)node)->value.xml);
+            break;
+        case LYD_ANYDATA_LYB:
+            free(((struct lyd_node_anydata *)node)->value.mem);
+            break;
+        case LYD_ANYDATA_STRING:
+        case LYD_ANYDATA_SXMLD:
+        case LYD_ANYDATA_JSOND:
+        case LYD_ANYDATA_LYBD:
+            /* dynamic strings are used only as input parameters */
+            assert(0);
+            break;
+        }
+#endif
+    } else if (node->schema->nodetype & LYD_NODE_TERM) {
+        lyd_free_value(ctx, &((struct lyd_node_term*)node)->value, ((struct lysc_node_leaf*)node->schema)->type);
+    }
+
+    /* free the node's attributes */
+    lyd_free_attr(ctx, node->attr, 1);
+
+    /* unlink only the nodes from the first level, nodes in subtree are freed all, so no unlink is needed */
+    if (top) {
+        lyd_unlink_tree(node);
+    }
+
+    free(node);
+}
+
+API void
+lyd_free_tree(struct lyd_node *node)
+{
+    if (!node) {
+        return;
+    }
+
+    lyd_free_subtree(node->schema->module->ctx, node, 1);
+}
+
+API void
+lyd_free_all(struct lyd_node *node)
+{
+    struct lyd_node *iter, *next;
+
+    if (!node) {
+        return;
+    }
+
+    /* get the first top-level sibling */
+    for (; node->parent; node = (struct lyd_node*)node->parent);
+    while (node->prev->next) {
+        node = node->prev;
+    }
+
+    LY_LIST_FOR_SAFE(node, next, iter) {
+        /* in case of the top-level nodes (node->parent is NULL), no unlinking needed */
+        lyd_free_subtree(iter->schema->module->ctx, iter, iter->parent ? 1 : 0);
+    }
+}
diff --git a/src/tree_data_hash.c b/src/tree_data_hash.c
new file mode 100644
index 0000000..546b2fd
--- /dev/null
+++ b/src/tree_data_hash.c
@@ -0,0 +1,25 @@
+/**
+ * @file tree_data_hash.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Functions to manipulate with the data node's hashes.
+ *
+ * Copyright (c) 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
+ */
+#include "common.h"
+
+#include "hash_table.h"
+#include "tree_data.h"
+
+void
+lyd_unlink_hash(struct lyd_node *node)
+{
+    if (node->parent && node->parent->children_ht) {
+        lyht_remove(node->parent->children_ht, &node, node->hash);
+    }
+}
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
new file mode 100644
index 0000000..fb9c3da
--- /dev/null
+++ b/src/tree_data_helpers.c
@@ -0,0 +1,126 @@
+/**
+ * @file tree_data_helpers.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Parsing and validation helper functions for data trees
+ *
+ * Copyright (c) 2015 - 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 "common.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "dict.h"
+#include "plugins_types.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+struct lyd_node **
+lyd_node_children_p(struct lyd_node *node)
+{
+    assert(node);
+    switch (node->schema->nodetype) {
+    case LYS_CONTAINER:
+    case LYS_LIST:
+        return &((struct lyd_node_inner*)node)->child;
+    default:
+        return NULL;
+    }
+}
+
+API const struct lyd_node *
+lyd_node_children(const struct lyd_node *node)
+{
+    struct lyd_node **children;
+
+    if (!node) {
+        return NULL;
+    }
+
+    children = lyd_node_children_p((struct lyd_node*)node);
+    if (children) {
+        return *children;
+    } else {
+        return NULL;
+    }
+}
+
+LY_ERR
+lyd_parse_check_options(struct ly_ctx *ctx, int options, const char *func)
+{
+    int x = options & LYD_OPT_TYPEMASK;
+
+    /* LYD_OPT_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG */
+    if (options & LYD_OPT_WHENAUTODEL) {
+        if ((x == LYD_OPT_EDIT) || (x == LYD_OPT_NOTIF_FILTER)) {
+            LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG)",
+                   func, options);
+            return LY_EINVAL;
+        }
+    }
+
+    if (options & (LYD_OPT_DATA_ADD_YANGLIB | LYD_OPT_DATA_NO_YANGLIB)) {
+        if (x != LYD_OPT_DATA) {
+            LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_*_YANGLIB can be used only with LYD_OPT_DATA)",
+                   func, options);
+            return LY_EINVAL;
+        }
+    }
+
+    /* "is power of 2" algorithm, with 0 exception */
+    if (x && !(x && !(x & (x - 1)))) {
+        LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (multiple data type flags set).", func, options);
+        return LY_EINVAL;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct ly_err_item *err = NULL;
+    struct ly_ctx *ctx;
+    struct lysc_type *type;
+    void *priv = NULL;
+    int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_STORE | (dynamic ? LY_TYPE_OPTS_DYNAMIC : 0);
+    assert(node);
+
+    ctx = node->schema->module->ctx;
+    type = ((struct lysc_node_leaf*)node->schema)->type;
+    if (type->plugin->validate) {
+        ret = type->plugin->validate(ctx, type, value, value_len, options, &node->value.canonized, &err, &priv);
+        if (ret) {
+            ly_err_print(err);
+            LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+            ly_err_free(err);
+            goto error;
+        }
+    } else if (dynamic) {
+        node->value.canonized = lydict_insert_zc(ctx, (char*)value);
+    } else {
+        node->value.canonized = lydict_insert(ctx, value, value_len);
+    }
+
+    if (type->plugin->store) {
+        ret = type->plugin->store(ctx, type, options, &node->value, &err, &priv);
+        if (ret) {
+            ly_err_print(err);
+            LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+            ly_err_free(err);
+            goto error;
+        }
+    }
+
+error:
+    return ret;
+}
+
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
new file mode 100644
index 0000000..ec11578
--- /dev/null
+++ b/src/tree_data_internal.h
@@ -0,0 +1,78 @@
+/**
+ * @file tree_data_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief internal functions for YANG schema trees.
+ *
+ * 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
+ */
+
+#ifndef LY_TREE_DATA_INTERNAL_H_
+#define LY_TREE_DATA_INTERNAL_H_
+
+#include "tree_data.h"
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ *
+ * Decides the node's type and in case it has a children list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+struct lyd_node **lyd_node_children_p(struct lyd_node *node);
+
+/**
+ * @brief Check validity of data parser options.
+ *
+ * @param ctx libyang context
+ * @param options Data parser options to check
+ * @param func name of the function where called
+ * @return LY_SUCCESS when options are ok
+ * @return LY_EINVAL when multiple data types bits are set, or incompatible options are used together.
+ */
+LY_ERR lyd_parse_check_options(struct ly_ctx *ctx, int options, const char *func);
+
+/**
+ * @brief Validate, canonize and store the given @p value into the node according to the node's type's rules.
+ *
+ * @param[in] node Data node for with the @p value.
+ * @param[in] value String value to be parsed.
+ * @param[in] value_len Length of the give @p value (mandatory).
+ * @param[in] dynamic Flag if @p value is a dynamically allocated memory and should be directly consumed/freed inside the function.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic);
+
+/**
+ * @brief Parse XML string as YANG data tree.
+ *
+ * @param[in] ctx libyang context
+ * @param[in] data Pointer to the XML string representation of the YANG data to parse.
+ * @param[in] options @ref dataparseroptions
+ * @param[out] result Resulting list of the parsed data trees. Note that NULL can be a valid result.
+ * @reutn LY_ERR value.
+ */
+LY_ERR lyd_parse_xml(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **result);
+
+/**
+ * @defgroup datahash Data nodes hash manipulation
+ * @ingroup datatree
+ */
+
+/**
+ * @brief Maintain node's parent's children hash table when unlinking the node.
+ *
+ * When completely freeing data tree, it is expected to free the parent's children hash table first, at once.
+ *
+ * @param[in] node The data node being unlinked from its parent.
+ */
+void lyd_unlink_hash(struct lyd_node *node);
+
+/** @} datahash */
+
+#endif /* LY_TREE_DATA_INTERNAL_H_ */
diff --git a/src/tree_schema.c b/src/tree_schema.c
index cabccb2..4e5ffb3 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -14,19 +14,25 @@
 
 #include "common.h"
 
+#include <assert.h>
 #include <dirent.h>
 #include <errno.h>
-#include <limits.h>
 #include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 
-#include "libyang.h"
 #include "context.h"
+#include "dict.h"
+#include "log.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_schema.h"
 #include "tree_schema_internal.h"
-#include "xpath.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)
@@ -40,6 +46,7 @@
 
     LY_CHECK_ARG_RET(NULL, parent || module, NULL);
 
+next:
     if (!last) {
         /* first call */
 
@@ -47,17 +54,15 @@
         if (parent) {
             /* schema subtree */
             if (parent->nodetype == LYS_CHOICE && (options & LYS_GETNEXT_WITHCASE)) {
-                if (!((struct lysc_node_choice*)parent)->cases) {
-                    return NULL;
+                if (((struct lysc_node_choice*)parent)->cases) {
+                    next = last = (const struct lysc_node*)&((struct lysc_node_choice*)parent)->cases[0];
                 }
-                next = last = (const struct lysc_node*)&((struct lysc_node_choice*)parent)->cases[0];
             } else {
                 snode = lysc_node_children_p(parent, (options & LYS_GETNEXT_OUTPUT) ? LYS_CONFIG_R : LYS_CONFIG_W);
                 /* do not return anything if the node does not have any children */
-                if (!snode || !(*snode)) {
-                    return NULL;
+                if (snode && *snode) {
+                    next = last = *snode;
                 }
-                next = last = *snode;
             }
         } else {
             /* top level data */
@@ -115,7 +120,7 @@
         /* possibly go back to parent */
         if (last && last->parent != parent) {
             last = last->parent;
-            next = last->next;
+            goto next;
         } else if (!action_flag) {
             action_flag = 1;
             next = parent ? (struct lysc_node*)lysc_node_actions(parent) : (struct lysc_node*)module->rpcs;
@@ -165,7 +170,7 @@
         goto repeat;
     default:
         /* we should not be here */
-        LOGINT(last->module->ctx);
+        LOGINT(module ? module->mod->ctx : parent->module->ctx);
         return NULL;
     }
 
@@ -218,8 +223,8 @@
     return feature->flags & LYS_FENABLED ? 1 : 0;
 }
 
-static uint8_t
-iff_getop(uint8_t *list, int pos)
+uint8_t
+lysc_iff_getop(uint8_t *list, int pos)
 {
     uint8_t *item;
     uint8_t mask = 3, result;
@@ -237,7 +242,7 @@
     uint8_t op;
     int a, b;
 
-    op = iff_getop(iff->expr, *index_e);
+    op = lysc_iff_getop(iff->expr, *index_e);
     (*index_e)++;
 
     switch (op) {
@@ -298,20 +303,25 @@
     struct ly_set *changed;
     struct ly_ctx *ctx = mod->ctx; /* shortcut */
 
+    if (!strcmp(name, "*")) {
+        /* enable all */
+        all = 1;
+    }
+
     if (!mod->compiled) {
         LOGERR(ctx, LY_EINVAL, "Module \"%s\" is not implemented so all its features are permanently disabled without a chance to change it.",
                mod->name);
         return LY_EINVAL;
     }
     if (!mod->compiled->features) {
+        if (all) {
+            /* no feature to enable */
+            return LY_SUCCESS;
+        }
         LOGERR(ctx, LY_EINVAL, "Unable to switch feature since the module \"%s\" has no features.", mod->name);
         return LY_EINVAL;
     }
 
-    if (!strcmp(name, "*")) {
-        /* enable all */
-        all = 1;
-    }
     changed = ly_set_new();
     changed_count = 0;
 
@@ -428,19 +438,19 @@
 }
 
 API LY_ERR
-lys_feature_enable(struct lys_module *module, const char *feature)
+lys_feature_enable(const struct lys_module *module, const char *feature)
 {
     LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
 
-    return lys_feature_change(module, feature, 1);
+    return lys_feature_change((struct lys_module*)module, feature, 1);
 }
 
 API LY_ERR
-lys_feature_disable(struct lys_module *module, const char *feature)
+lys_feature_disable(const struct lys_module *module, const char *feature)
 {
     LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
 
-    return lys_feature_change(module, feature, 0);
+    return lys_feature_change((struct lys_module*)module, feature, 0);
 }
 
 API int
@@ -501,12 +511,12 @@
 }
 
 struct lysp_submodule *
-lys_parse_mem_submodule(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, struct ly_parser_ctx *main_ctx,
+lys_parse_mem_submodule(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
                         LY_ERR (*custom_check)(struct ly_ctx*, struct lysp_module*, struct lysp_submodule*, void*), void *check_data)
 {
     LY_ERR ret = LY_EINVAL;
     struct lysp_submodule *submod = NULL, *latest_sp;
-    struct ly_parser_ctx context = {0};
+    struct lys_parser_ctx context = {0};
 
     LY_CHECK_ARG_RET(ctx, ctx, data, NULL);
 
@@ -518,11 +528,11 @@
     memcpy(&context.grps_nodes, &main_ctx->grps_nodes, sizeof main_ctx->grps_nodes);
 
     switch (format) {
+    /* TODO not yet supported
     case LYS_IN_YIN:
-        /* TODO not yet supported
         mod = yin_read_module();
-        */
         break;
+    */
     case LYS_IN_YANG:
         ret = yang_parse_submodule(&context, data, &submod);
         break;
@@ -578,7 +588,7 @@
     struct lysp_include *inc;
     LY_ERR ret = LY_EINVAL;
     unsigned int u, i;
-    struct ly_parser_ctx context = {0};
+    struct lys_parser_ctx context = {0};
 
     LY_CHECK_ARG_RET(ctx, ctx, data, NULL);
 
@@ -590,11 +600,11 @@
     mod->ctx = ctx;
 
     switch (format) {
+    /* TODO not yet supported
     case LYS_IN_YIN:
-        /* TODO not yet supported
         mod = yin_read_module();
-        */
         break;
+    */
     case LYS_IN_YANG:
         ret = yang_parse_module(&context, data, mod);
         break;
@@ -663,7 +673,7 @@
 
     if (!mod->implemented) {
         /* pre-compile features of the module */
-        LY_CHECK_GOTO(lys_feature_precompile(ctx, mod->parsed->features, &mod->off_features), error);
+        LY_CHECK_GOTO(lys_feature_precompile(ctx, mod, mod->parsed->features, &mod->off_features), error);
     }
 
     /* decide the latest revision */
@@ -709,7 +719,7 @@
         }
         if (!mod->implemented) {
             /* pre-compile features of the module */
-            LY_CHECK_GOTO(lys_feature_precompile(ctx, inc->submodule->features, &mod->off_features), error);
+            LY_CHECK_GOTO(lys_feature_precompile(ctx, mod, inc->submodule->features, &mod->off_features), error);
         }
     }
     mod->parsed->parsing = 0;
@@ -765,7 +775,7 @@
 }
 
 void *
-lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct lys_parser_ctx *main_ctx,
                     LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data),
                     void *check_data)
 {
@@ -817,7 +827,7 @@
 }
 
 struct lysp_submodule *
-lys_parse_fd_submodule(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct ly_parser_ctx *main_ctx,
+lys_parse_fd_submodule(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
                        LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data),
                        void *check_data)
 {
@@ -1005,10 +1015,12 @@
 
                 /* get type according to filename suffix */
                 flen = strlen(file->d_name);
-                if (!strcmp(&file->d_name[flen - 4], ".yin")) {
-                    format_aux = LYS_IN_YIN;
-                } else if (!strcmp(&file->d_name[flen - 5], ".yang")) {
+                if (!strcmp(&file->d_name[flen - 5], ".yang")) {
                     format_aux = LYS_IN_YANG;
+                /* TODO YIN parser
+                } else if (!strcmp(&file->d_name[flen - 4], ".yin")) {
+                    format_aux = LYS_IN_YIN;
+                */
                 } else {
                     /* not supportde suffix/file format */
                     continue;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index ea5d3e1..04f7d18 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -15,124 +15,29 @@
 #ifndef LY_TREE_SCHEMA_H_
 #define LY_TREE_SCHEMA_H_
 
-#include <pcre.h>
-#include <stdint.h>
+#define PCRE2_CODE_UNIT_WIDTH 8
 
+#include <pcre2.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "log.h"
+#include "tree.h"
 #include "extensions.h"
 
+struct ly_ctx;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /**
- * @brief XPath representation.
- */
-struct lyxp_expr;
-
-/**
- * @brief Macro selector for other LY_ARRAY_* macros, do not use directly!
- */
-#define LY_ARRAY_SELECT(_1, _2, NAME, ...) NAME
-
-/**
- * @brief Helper macro to go through sized-arrays with a pointer iterator.
- *
- * Use with opening curly bracket (`{`).
- *
- * @param[in] ARRAY Array to go through
- * @param[in] TYPE Type of the records in the ARRAY
- * @param[out] ITER Iterating pointer to the item being processed in each loop
- */
-#define LY_ARRAY_FOR_ITER(ARRAY, TYPE, ITER) \
-    for (ITER = ARRAY; \
-         (ARRAY) && ((void*)ITER - (void*)ARRAY)/(sizeof(TYPE)) < (*((uint32_t*)(ARRAY) - 1)); \
-         ITER = (void*)((TYPE*)ITER + 1))
-
-/**
- * @brief Helper macro to go through sized-arrays with a numeric iterator.
- *
- * Use with opening curly bracket (`{`).
- *
- * To access an item with the INDEX value, use always LY_ARRAY_INDEX macro!
- *
- * @param[in] ARRAY Array to go through
- * @param[out] INDEX Iterating index of the item being processed in each loop
- */
-#define LY_ARRAY_FOR_INDEX(ARRAY, INDEX) \
-    for (INDEX = 0; \
-         ARRAY && INDEX < (*((uint32_t*)(ARRAY) - 1)); \
-         ++INDEX)
-
-/**
- * @defgroup schematree Schema Tree
- * @{
- *
- * Data structures and functions to manipulate and access schema tree.
- */
-
-/**
- * @brief Get a number of records in the ARRAY.
- *
- * Does not check if array exists!
- */
-#define LY_ARRAY_SIZE(ARRAY) (*((uint32_t*)(ARRAY) - 1))
-
-/**
- * @brief Sized-array iterator (for-loop).
- *
- * Use with opening curly bracket (`{`).
- *
- * There are 2 variants:
- *
- *     LY_ARRAY_FOR(ARRAY, TYPE, ITER)
- *
- * Where ARRAY is a sized-array to go through, TYPE is the type of the items in the ARRAY and ITER is a pointer variable
- * providing the items of the ARRAY in the loops. This functionality is provided by LY_ARRAY_FOR_ITER macro
- *
- *     LY_ARRAY_FOR(ARRAY, INDEX)
- *
- * The ARRAY is again a sized-array to go through, the INDEX is a variable (unsigned integer) for storing iterating ARRAY's index
- * to access the items of ARRAY in the loops. This functionality is provided by LY_ARRAY_FOR_INDEX macro.
- */
-#define LY_ARRAY_FOR(ARRAY, ...) LY_ARRAY_SELECT(__VA_ARGS__, LY_ARRAY_FOR_ITER, LY_ARRAY_FOR_INDEX)(ARRAY, __VA_ARGS__)
-
-/**
- * @brief Macro to iterate via all sibling elements without affecting the list itself
- *
- * Works for all types of nodes despite it is data or schema tree, but all the
- * parameters must be pointers to the same type.
- *
- * Use with opening curly bracket (`{`). All parameters must be of the same type.
- *
- * @param START Pointer to the starting element.
- * @param ELEM Iterator.
- */
-#define LY_LIST_FOR(START, ELEM) \
-    for ((ELEM) = (START); \
-         (ELEM); \
-         (ELEM) = (ELEM)->next)
-
-/**
- * @brief Macro to iterate via all sibling elements allowing to modify the list itself (e.g. removing elements)
- *
- * Use with opening curly bracket (`{`). All parameters must be of the same type.
- *
- * @param START Pointer to the starting element.
- * @param NEXT Temporary storage to allow removing of the current iterator content.
- * @param ELEM Iterator.
- */
-#define LY_LIST_FOR_SAFE(START, NEXT, ELEM) \
-    for ((ELEM) = (START); \
-         (ELEM) ? (NEXT = (ELEM)->next, 1) : 0; \
-         (ELEM) = (NEXT))
-
-/**
  * @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 = 2       /**< YIN schema input format */
+    LYS_IN_YIN = 3       /**< YIN schema input format */
 } LYS_INFORMAT;
 
 /**
@@ -141,7 +46,9 @@
 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_YIN = 2,     /**< YIN schema output format */
+    LYS_OUT_YIN = 3,     /**< YIN schema output format */
+    LYS_OUT_YANG_COMPILED = 2, /**< YANG schema output format of the compiled schema tree */
+
     LYS_OUT_TREE,        /**< Tree schema output format, for more information see the [printers](@ref howtoschemasprinters) page */
     LYS_OUT_INFO,        /**< Info schema output format, for more information see the [printers](@ref howtoschemasprinters) page */
     LYS_OUT_JSON,        /**< JSON schema output format, reflecting YIN format with conversion of attributes to object's members */
@@ -158,47 +65,18 @@
 #define LYS_ANYXML 0x0020         /**< anyxml statement node */
 #define LYS_ANYDATA 0x0120        /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
 
-#define LYS_CASE 0x0040           /**< case statement node */
-#define LYS_USES 0x0080           /**< uses statement node */
-#define LYS_INOUT 0x200
 #define LYS_ACTION 0x400          /**< RPC or action */
 #define LYS_NOTIF 0x800
+
+#define LYS_CASE 0x0040           /**< case statement node */
+#define LYS_USES 0x0080           /**< uses statement node */
+#define LYS_INPUT 0x100
+#define LYS_OUTPUT 0x200
+#define LYS_INOUT 0x300
 #define LYS_GROUPING 0x1000
 #define LYS_AUGMENT 0x2000
 
 /**
- * @brief YANG built-in types
- */
-typedef enum {
-    LY_TYPE_UNKNOWN = 0,  /**< Unknown type */
-    LY_TYPE_BINARY,       /**< Any binary data ([RFC 6020 sec 9.8](http://tools.ietf.org/html/rfc6020#section-9.8)) */
-    LY_TYPE_UINT8,        /**< 8-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
-    LY_TYPE_UINT16,       /**< 16-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
-    LY_TYPE_UINT32,       /**< 32-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
-    LY_TYPE_UINT64,       /**< 64-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
-    LY_TYPE_STRING,       /**< Human-readable string ([RFC 6020 sec 9.4](http://tools.ietf.org/html/rfc6020#section-9.4)) */
-    LY_TYPE_BITS,         /**< A set of bits or flags ([RFC 6020 sec 9.7](http://tools.ietf.org/html/rfc6020#section-9.7)) */
-    LY_TYPE_BOOL,         /**< "true" or "false" ([RFC 6020 sec 9.5](http://tools.ietf.org/html/rfc6020#section-9.5)) */
-    LY_TYPE_DEC64,        /**< 64-bit signed decimal number ([RFC 6020 sec 9.3](http://tools.ietf.org/html/rfc6020#section-9.3))*/
-    LY_TYPE_EMPTY,        /**< A leaf that does not have any value ([RFC 6020 sec 9.11](http://tools.ietf.org/html/rfc6020#section-9.11)) */
-    LY_TYPE_ENUM,         /**< Enumerated strings ([RFC 6020 sec 9.6](http://tools.ietf.org/html/rfc6020#section-9.6)) */
-    LY_TYPE_IDENT,        /**< A reference to an abstract identity ([RFC 6020 sec 9.10](http://tools.ietf.org/html/rfc6020#section-9.10)) */
-    LY_TYPE_INST,         /**< References a data tree node ([RFC 6020 sec 9.13](http://tools.ietf.org/html/rfc6020#section-9.13)) */
-    LY_TYPE_LEAFREF,      /**< A reference to a leaf instance ([RFC 6020 sec 9.9](http://tools.ietf.org/html/rfc6020#section-9.9))*/
-    LY_TYPE_UNION,        /**< Choice of member types ([RFC 6020 sec 9.12](http://tools.ietf.org/html/rfc6020#section-9.12)) */
-    LY_TYPE_INT8,         /**< 8-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
-    LY_TYPE_INT16,        /**< 16-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
-    LY_TYPE_INT32,        /**< 32-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
-    LY_TYPE_INT64,        /**< 64-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
-} LY_DATA_TYPE;
-#define LY_DATA_TYPE_COUNT 20 /**< Number of different types */
-
-/**
- * @brief Stringified YANG built-in data types
- */
-extern const char* ly_data_type2str[LY_DATA_TYPE_COUNT];
-
-/**
  * @brief YANG import-stmt
  */
 struct lysp_import {
@@ -245,6 +123,7 @@
     const char *arg;                 /**< statement's argument */
     struct lysp_stmt *next;          /**< link to the next statement */
     struct lysp_stmt *child;         /**< list of the statement's substatements (linked list) */
+    uint16_t flags;
 };
 
 /**
@@ -253,10 +132,10 @@
 struct lysp_ext_instance {
     const char *name;                /**< extension identifier, including possible prefix */
     const char *argument;            /**< optional value of the extension's argument */
+    struct lysp_stmt *child;         /**< list of the extension's substatements (linked list) */
     LYEXT_SUBSTMT insubstmt;         /**< value identifying placement of the extension instance */
     uint32_t insubstmt_index;        /**< in case the instance is in a substatement, this identifies
                                           the index of that substatement */
-    struct lysp_stmt *child;         /**< list of the extension's substatements (linked list) */
 };
 
 /**
@@ -494,48 +373,51 @@
  *
  *     1 - container    6 - anydata/anyxml    11 - output       16 - grouping   21 - enum
  *     2 - choice       7 - case              12 - feature      17 - uses       22 - type
- *     3 - leaf         8 - notification      13 - identity     18 - refine
+ *     3 - leaf         8 - notification      13 - identity     18 - refine     23 - stmt
  *     4 - leaflist     9 - rpc               14 - extension    19 - augment
  *     5 - list        10 - input             15 - typedef      20 - deviate
  *
- *                                             1 1 1 1 1 1 1 1 1 1 2 2 2
- *     bit name              1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
- *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       1 LYS_CONFIG_W     |x|x|x|x|x|x|x| | | | | | | | | | |x| |x| | |
- *         LYS_SET_BASE     | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       2 LYS_CONFIG_R     |x|x|x|x|x|x|x| | | | | | | | | | |x| |x| | |
- *         LYS_SET_BIT      | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       3 LYS_STATUS_CURR  |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
- *         LYS_SET_ENUM     | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
- *         LYS_SET_FRDIGITS | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
- *         LYS_SET_LENGTH   | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       6 LYS_MAND_TRUE    | |x|x| | |x| | | | | | | | | | | |x| |x| | |
- *         LYS_SET_PATH     | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       7 LYS_MAND_FALSE   | |x|x| | |x| | | | | | | | | | | |x| |x| | |
- *         LYS_ORDBY_USER   | | | |x|x| | | | | | | | | | | | | | | | | |
- *         LYS_SET_PATTERN  | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | | | | | | | | | |
- *         LYS_YINELEM_TRUE | | | | | | | | | | | | | |x| | | | | | | | |
- *         LYS_SET_RANGE    | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       9 LYS_YINELEM_FALSE| | | | | | | | | | | | | |x| | | | | | | | |
- *         LYS_SET_TYPE     | | | | | | | | | | | | | | | | | | | | | |x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *      10 LYS_SET_VALUE    | | | | | | | | | | | | | | | | | | | | |x| |
- *         LYS_SET_REQINST  | | | | | | | | | | | | | | | | | | | | | |x|
- *         LYS_SET_MIN      | | | |x|x| | | | | | | | | | | | |x| |x| | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *      11 LYS_SET_MAX      | | | |x|x| | | | | | | | | | | | |x| |x| | |
- *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *                                             1 1 1 1 1 1 1 1 1 1 2 2 2 2
+ *     bit name              1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       1 LYS_CONFIG_W     |x|x|x|x|x|x|x| | | | | | | | | | |x| |x| | | |
+ *         LYS_SET_BASE     | | | | | | | | | | | | | | | | | | | | | |x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       2 LYS_CONFIG_R     |x|x|x|x|x|x|x| | | | | | | | | | |x| |x| | | |
+ *         LYS_SET_BIT      | | | | | | | | | | | | | | | | | | | | | |x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       3 LYS_STATUS_CURR  |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| | |
+ *         LYS_SET_ENUM     | | | | | | | | | | | | | | | | | | | | | |x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| | |
+ *         LYS_SET_FRDIGITS | | | | | | | | | | | | | | | | | | | | | |x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| | |
+ *         LYS_SET_LENGTH   | | | | | | | | | | | | | | | | | | | | | |x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       6 LYS_MAND_TRUE    | |x|x| | |x| | | | | | | | | | | |x| |x| | | |
+ *         LYS_SET_PATH     | | | | | | | | | | | | | | | | | | | | | |x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       7 LYS_MAND_FALSE   | |x|x| | |x| | | | | | | | | | | |x| |x| | | |
+ *         LYS_ORDBY_USER   | | | |x|x| | | | | | | | | | | | | | | | | | |
+ *         LYS_SET_PATTERN  | | | | | | | | | | | | | | | | | | | | | |x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | | | | | | | | | | |
+ *         LYS_YINELEM_TRUE | | | | | | | | | | | | | |x| | | | | | | | | |
+ *         LYS_SET_RANGE    | | | | | | | | | | | | | | | | | | | | | |x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       9 LYS_YINELEM_FALSE| | | | | | | | | | | | | |x| | | | | | | | | |
+ *         LYS_SET_TYPE     | | | | | | | | | | | | | | | | | | | | | |x| |
+ *         LYS_SINGLEQUOTED | | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *      10 LYS_SET_VALUE    | | | | | | | | | | | | | | | | | | | | |x| | |
+ *         LYS_SET_REQINST  | | | | | | | | | | | | | | | | | | | | | |x| |
+ *         LYS_SET_MIN      | | | |x|x| | | | | | | | | | | | |x| |x| | | |
+ *         LYS_DOUBLEQUOTED | | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *      11 LYS_SET_MAX      | | | |x|x| | | | | | | | | | | | |x| |x| | | |
+ *         LYS_USED_GRP     | | | | | | | | | | | | | | | |x| | | | | | | |
+ *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *
  */
 
@@ -549,39 +431,40 @@
  *     2 - choice       7 - case              12 - feature
  *     3 - leaf         8 - notification      13 - identity
  *     4 - leaflist     9 - rpc               14 - extension
- *     5 - list        10 - input
+ *     5 - list        10 - input             15 - bitenum
  *
- *                                             1 1 1 1 1
- *     bit name              1 2 3 4 5 6 7 8 9 0 1 2 3 4
- *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       1 LYS_CONFIG_W     |x|x|x|x|x|x|x| | | | | | | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       2 LYS_CONFIG_R     |x|x|x|x|x|x|x| | | | | | | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       3 LYS_STATUS_CURR  |x|x|x|x|x|x|x|x|x| | |x|x|x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x|
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       6 LYS_MAND_TRUE    |x|x|x|x|x|x| | | | | | | | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       7 LYS_ORDBY_USER   | | | |x|x| | | | | | | | | |
- *         LYS_MAND_FALSE   | |x|x| | |x| | | | | | | | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | |
- *         LYS_PRESENCE     |x| | | | | | | | | | | | | |
- *         LYS_UNIQUE       | | |x| | | | | | | | | | | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       9 LYS_KEY          | | |x| | | | | | | | | | | |
- *         LYS_FENABLED     | | | | | | | | | | | |x| | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *      10 LYS_SET_DFLT     | | |x|x| | |x| | | | | | | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *      11 LYS_SET_UNITS    | | |x|x| | | | | | | | | | |
- *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *      11 LYS_SET_CONFIG   |x|x|x|x|x|x|x| | |x|x| | | |
- *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *                                             1 1 1 1 1 1
+ *     bit name              1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       1 LYS_CONFIG_W     |x|x|x|x|x|x|x| | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       2 LYS_CONFIG_R     |x|x|x|x|x|x|x| | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       3 LYS_STATUS_CURR  |x|x|x|x|x|x|x|x|x| | |x|x|x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x| |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       6 LYS_MAND_TRUE    |x|x|x|x|x|x| | | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       7 LYS_ORDBY_USER   | | | |x|x| | | | | | | | | | |
+ *         LYS_MAND_FALSE   | |x|x| | |x| | | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | | |
+ *         LYS_PRESENCE     |x| | | | | | | | | | | | | | |
+ *         LYS_UNIQUE       | | |x| | | | | | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       9 LYS_KEY          | | |x| | | | | | | | | | | | |
+ *         LYS_FENABLED     | | | | | | | | | | | |x| | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *      10 LYS_SET_DFLT     | | |x|x| | |x| | | | | | | | |
+ *         LYS_ISENUM       | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *      11 LYS_SET_UNITS    | | |x|x| | | | | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *      11 LYS_SET_CONFIG   |x|x|x|x|x|x|x| | |x|x| | | | |
+ *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *
  */
 
@@ -618,6 +501,8 @@
 #define LYS_YINELEM_TRUE 0x80        /**< yin-element true for extension's argument */
 #define LYS_YINELEM_FALSE 0x100      /**< yin-element false for extension's argument */
 #define LYS_YINELEM_MASK 0x180       /**< mask for yin-element value */
+#define LYS_USED_GRP     0x400       /**< internal flag for validating not-instantiated groupings
+                                          (resp. do not validate again the instantiated groupings). */
 #define LYS_SET_VALUE    0x200       /**< value attribute is set */
 #define LYS_SET_MIN      0x200       /**< min attribute is set */
 #define LYS_SET_MAX      0x400       /**< max attribute is set */
@@ -640,6 +525,11 @@
 #define LYS_SET_UNITS    0x0400      /**< flag to know if the leaf's/leaflist's units are their own (flag set) or it is taken from the type. */
 #define LYS_SET_CONFIG   0x0800      /**< flag to know if the config property was set explicitly (flag set) or it is inherited. */
 
+#define LYS_SINGLEQUOTED 0x100       /**< flag for single-quoted argument of an extension instance's substatement */
+#define LYS_DOUBLEQUOTED 0x200       /**< flag for double-quoted argument of an extension instance's substatement */
+
+#define LYS_ISENUM       0x200       /**< flag to simply distinguish type in struct lysc_type_bitenum_item */
+
 #define LYS_FLAGS_COMPILED_MASK 0xff /**< mask for flags that maps to the compiled structures */
 /** @} */
 
@@ -1000,9 +890,10 @@
     const char *name;                /**< identity name (mandatory), including possible prefix */
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lys_module *module;       /**< module structure */
     struct lysc_ident **derived;     /**< list of (pointers to the) derived identities ([sized array](@ref sizedarrays)) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ values are allowed */
 };
 
@@ -1013,9 +904,10 @@
     const char *name;                /**< feature name (mandatory) */
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
+    struct lys_module *module;       /**< module structure */
     struct lysc_feature **depfeatures;/**< list of pointers to other features depending on this one ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* and
                                           #LYS_FENABLED values allowed */
 };
@@ -1044,13 +936,13 @@
 
 struct lysc_range {
     struct lysc_range_part {
-        union {                      /**< min boundary TODO decimal */
-            int64_t min_64;          /**< for int8, int16, int32 and int64 */
-            uint64_t min_u64;        /**< for uint8, uint16, uint32 and uint64 */
+        union {                      /**< min boundary */
+            int64_t min_64;          /**< for int8, int16, int32, int64 and decimal64 ( >= LY_TYPE_DEC64) */
+            uint64_t min_u64;        /**< for uint8, uint16, uint32, uint64, string and binary ( < LY_TYPE_DEC64) */
         };
-        union {                      /**< max boundary TODO decimal */
-            int64_t max_64;          /**< for int8, int16, int32 and int64 */
-            uint64_t max_u64;        /**< for uint8, uint16, uint32 and uint64 */
+        union {                      /**< max boundary */
+            int64_t max_64;          /**< for int8, int16, int32, int64 and decimal64 ( >= LY_TYPE_DEC64) */
+            uint64_t max_u64;        /**< for uint8, uint16, uint32, uint64, string and binary ( < LY_TYPE_DEC64) */
         };
     } *parts;                        /**< compiled range expression ([sized array](@ref sizedarrays)) */
     const char *dsc;                 /**< description */
@@ -1061,8 +953,8 @@
 };
 
 struct lysc_pattern {
-    pcre *expr;                      /**< compiled regular expression */
-    pcre_extra *expr_extra;          /**< additional information to speed up matching */
+    const char *expr;                /**< original, not compiled, regular expression */
+    pcre2_code *code;                /**< compiled regular expression */
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     const char *emsg;                /**< error-message */
@@ -1085,6 +977,7 @@
 struct lysc_type {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
 };
@@ -1092,6 +985,7 @@
 struct lysc_type_num {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
     struct lysc_range *range;        /**< Optional range limitation */
@@ -1100,6 +994,7 @@
 struct lysc_type_dec {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
     uint8_t fraction_digits;         /**< fraction digits specification */
@@ -1109,30 +1004,50 @@
 struct lysc_type_str {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
     struct lysc_range *length;       /**< Optional length limitation */
     struct lysc_pattern **patterns;  /**< Optional list of pointers to pattern limitations ([sized array](@ref sizedarrays)) */
 };
 
+struct lysc_type_bitenum_item {
+    const char *name;            /**< enumeration identifier */
+    const char *dsc;             /**< description */
+    const char *ref;             /**< reference */
+    struct lysc_ext_instance *exts;    /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    union {
+        int32_t value;           /**< integer value associated with the enumeration */
+        uint32_t position;       /**< non-negative integer value associated with the bit */
+    };
+    uint16_t flags;              /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ and LYS_SET_VALUE
+                                          values are allowed */
+};
+
 struct lysc_type_enum {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
-    struct lysc_type_enum_item {
-        const char *name;            /**< enumeration identifier */
-        const char *dsc;             /**< description */
-        const char *ref;             /**< reference */
-        struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
-        struct lysc_ext_instance *exts;    /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-        int32_t value;               /**< integer value associated with the enumeration */
-    } *enums;                        /**< enumerations list ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+    struct lysc_type_bitenum_item *enums; /**< enumerations list ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_bits {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    struct lysc_type_bitenum_item *bits; /**< bits list ([sized array](@ref sizedarrays)), mandatory (at least 1 item),
+                                              the items are ordered by their position value. */
 };
 
 struct lysc_type_leafref {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
     const char* path;                /**< target path */
@@ -1144,6 +1059,7 @@
 struct lysc_type_identityref {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
     struct lysc_ident **bases;       /**< list of pointers to the base identities ([sized array](@ref sizedarrays)),
@@ -1153,29 +1069,16 @@
 struct lysc_type_instanceid {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
     uint8_t require_instance;        /**< require-instance flag */
 };
 
-struct lysc_type_bits {
-    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    const char *dflt;                /**< type's default value if any */
-    LY_DATA_TYPE basetype;           /**< Base type of the type */
-    uint32_t refcount;               /**< reference counter for type sharing */
-    struct lysc_type_bits_item {
-        const char *name;            /**< bit identifier */
-        const char *dsc;             /**< description */
-        const char *ref;             /**< reference */
-        struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
-        struct lysc_ext_instance *exts;    /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-        uint32_t position;           /**< non-negative integer value associated with the bit */
-    } *bits;                         /**< bits list ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
-};
-
 struct lysc_type_union {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
     struct lysc_type **types;        /**< list of types in the union ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
@@ -1184,6 +1087,7 @@
 struct lysc_type_bin {
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     const char *dflt;                /**< type's default value if any */
+    struct lysc_type_plugin *plugin; /**< type's plugin with built-in as well as user functions to canonize or validate the value of the type */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
     struct lysc_range *length;       /**< Optional length limitation */
@@ -1191,7 +1095,6 @@
 
 struct lysc_action_inout {
     struct lysc_node *data;          /**< first child node (linked list) */
-    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
 };
 
@@ -1202,13 +1105,16 @@
     struct lysp_action *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node - RPC) */
 
-    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_ext_instance *input_exts;  /**< list of the extension instances of input ([sized array](@ref sizedarrays)) */
+    struct lysc_ext_instance *output_exts; /**< list of the extension instances of outpu ([sized array](@ref sizedarrays)) */
 
     const char *name;                /**< action/RPC name (mandatory) */
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
 
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+
     struct lysc_action_inout input;  /**< RPC's/action's input */
     struct lysc_action_inout output; /**< RPC's/action's output */
 
@@ -1218,13 +1124,18 @@
     uint16_t nodetype;               /**< LYS_NOTIF */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lys_module *module;       /**< module structure */
-    struct lysp_notification *sp;    /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+    struct lysp_notif *sp;           /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
 
+    struct lysc_node *data;          /**< first child node (linked list) */
+    struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+
     const char *name;                /**< Notification name (mandatory) */
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
-    /* TODO */
+
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 };
 
 /**
@@ -1245,8 +1156,8 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
 };
 
 struct lysc_node_container {
@@ -1264,8 +1175,8 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
 
     struct lysc_node *child;         /**< first child node (linked list) */
     struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
@@ -1288,8 +1199,8 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
 
     struct lysc_node *child;         /**< first child node of the case (linked list). Note that all the children of all the sibling cases are linked
                                           each other as siblings with the parent pointer pointing to appropriate case node. */
@@ -1310,8 +1221,8 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
 
     struct lysc_node_case *cases;    /**< list of the cases (linked list). Note that all the children of all the cases are linked each other
                                           as siblings. Their parent pointers points to the specific case they belongs to, so distinguish the
@@ -1334,8 +1245,8 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
 
     struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
     struct lysc_type *type;          /**< type of the leaf node (mandatory) */
@@ -1359,8 +1270,8 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
 
     struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
     struct lysc_type *type;          /**< type of the leaf node (mandatory) */
@@ -1387,8 +1298,8 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
 
     struct lysc_node *child;         /**< first child node (linked list) */
     struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
@@ -1416,8 +1327,8 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
 
     struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
 };
@@ -1577,7 +1488,7 @@
  * @param[in] feature Name of the feature to enable. To enable all features at once, use asterisk (`*`) character.
  * @return LY_ERR value.
  */
-LY_ERR lys_feature_enable(struct lys_module *module, const char *feature);
+LY_ERR lys_feature_enable(const struct lys_module *module, const char *feature);
 
 /**
  * @brief Disable specified feature in the module
@@ -1588,7 +1499,7 @@
  * @param[in] feature Name of the feature to disable. To disable all features at once, use asterisk (`*`) character.
  * @return LY_ERR value
  */
-LY_ERR lys_feature_disable(struct lys_module *module, const char *feature);
+LY_ERR lys_feature_disable(const struct lys_module *module, const char *feature);
 
 /**
  * @brief Get the current status of the specified feature in the module.
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 2c4ea54..2d73a01 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -14,11 +14,20 @@
 
 #include "common.h"
 
+#include <assert.h>
 #include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include "libyang.h"
-#include "context.h"
+#include "dict.h"
+#include "log.h"
+#include "set.h"
+#include "plugins_types.h"
+#include "tree.h"
+#include "tree_schema.h"
 #include "tree_schema_internal.h"
 #include "xpath.h"
 
@@ -30,53 +39,53 @@
  */
 #define DUP_STRING(CTX, ORIG, DUP) if (ORIG) {DUP = lydict_insert(CTX, ORIG, 0);}
 
-#define COMPILE_ARRAY_GOTO(CTX, ARRAY_P, ARRAY_C, OPTIONS, ITER, FUNC, RET, GOTO) \
+#define COMPILE_ARRAY_GOTO(CTX, ARRAY_P, ARRAY_C, ITER, FUNC, RET, GOTO) \
     if (ARRAY_P) { \
         LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
         size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
         for (ITER = 0; ITER < LY_ARRAY_SIZE(ARRAY_P); ++ITER) { \
             LY_ARRAY_INCREMENT(ARRAY_C); \
-            RET = FUNC(CTX, &(ARRAY_P)[ITER], OPTIONS, &(ARRAY_C)[ITER + __array_offset]); \
+            RET = FUNC(CTX, &(ARRAY_P)[ITER], &(ARRAY_C)[ITER + __array_offset]); \
             LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
         } \
     }
 
-#define COMPILE_ARRAY1_GOTO(CTX, ARRAY_P, ARRAY_C, PARENT, OPTIONS, ITER, FUNC, USES_STATUS, RET, GOTO) \
+#define COMPILE_ARRAY1_GOTO(CTX, ARRAY_P, ARRAY_C, PARENT, ITER, FUNC, USES_STATUS, RET, GOTO) \
     if (ARRAY_P) { \
         LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
         size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
         for (ITER = 0; ITER < LY_ARRAY_SIZE(ARRAY_P); ++ITER) { \
             LY_ARRAY_INCREMENT(ARRAY_C); \
-            RET = FUNC(CTX, &(ARRAY_P)[ITER], OPTIONS, PARENT, &(ARRAY_C)[ITER + __array_offset], USES_STATUS); \
+            RET = FUNC(CTX, &(ARRAY_P)[ITER], PARENT, &(ARRAY_C)[ITER + __array_offset], USES_STATUS); \
             LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
         } \
     }
 
-#define COMPILE_ARRAY_UNIQUE_GOTO(CTX, ARRAY_P, ARRAY_C, OPTIONS, ITER, FUNC, RET, GOTO) \
+#define COMPILE_ARRAY_UNIQUE_GOTO(CTX, ARRAY_P, ARRAY_C, ITER, FUNC, RET, GOTO) \
     if (ARRAY_P) { \
         LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
         size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
         for (ITER = 0; ITER < LY_ARRAY_SIZE(ARRAY_P); ++ITER) { \
             LY_ARRAY_INCREMENT(ARRAY_C); \
-            RET = FUNC(CTX, &(ARRAY_P)[ITER], OPTIONS, ARRAY_C, &(ARRAY_C)[ITER + __array_offset]); \
+            RET = FUNC(CTX, &(ARRAY_P)[ITER], ARRAY_C, &(ARRAY_C)[ITER + __array_offset]); \
             LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
         } \
     }
 
-#define COMPILE_MEMBER_GOTO(CTX, MEMBER_P, MEMBER_C, OPTIONS, FUNC, RET, GOTO) \
+#define COMPILE_MEMBER_GOTO(CTX, MEMBER_P, MEMBER_C, FUNC, RET, GOTO) \
     if (MEMBER_P) { \
         MEMBER_C = calloc(1, sizeof *(MEMBER_C)); \
         LY_CHECK_ERR_GOTO(!(MEMBER_C), LOGMEM((CTX)->ctx); RET = LY_EMEM, GOTO); \
-        RET = FUNC(CTX, MEMBER_P, OPTIONS, MEMBER_C); \
+        RET = FUNC(CTX, MEMBER_P, MEMBER_C); \
         LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
     }
 
-#define COMPILE_MEMBER_ARRAY_GOTO(CTX, MEMBER_P, ARRAY_C, OPTIONS, FUNC, RET, GOTO) \
+#define COMPILE_MEMBER_ARRAY_GOTO(CTX, MEMBER_P, ARRAY_C, FUNC, RET, GOTO) \
     if (MEMBER_P) { \
         LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, 1, RET, GOTO); \
         size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
         LY_ARRAY_INCREMENT(ARRAY_C); \
-        RET = FUNC(CTX, MEMBER_P, OPTIONS, &(ARRAY_C)[__array_offset]); \
+        RET = FUNC(CTX, MEMBER_P, &(ARRAY_C)[__array_offset]); \
         LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
     }
 
@@ -93,7 +102,7 @@
 static struct lysc_ext_instance *
 lysc_ext_instance_dup(struct ly_ctx *ctx, struct lysc_ext_instance *orig)
 {
-    /* TODO */
+    /* TODO - extensions */
     (void) ctx;
     (void) orig;
     return NULL;
@@ -302,7 +311,7 @@
 }
 
 static LY_ERR
-lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, int UNUSED(options), struct lysc_ext_instance *ext)
+lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext)
 {
     const char *name;
     unsigned int u;
@@ -344,12 +353,11 @@
  * @brief Compile information from the if-feature statement
  * @param[in] ctx Compile context.
  * @param[in] value The if-feature argument to process. It is pointer-to-pointer-to-char just to unify the compile functions.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] iff Prepared (empty) compiled if-feature structure to fill.
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_compile_iffeature(struct lysc_ctx *ctx, const char **value, int UNUSED(options), struct lysc_iffeature *iff)
+lys_compile_iffeature(struct lysc_ctx *ctx, const char **value, struct lysc_iffeature *iff)
 {
     const char *c = *value;
     int r, rc = EXIT_FAILURE;
@@ -523,12 +531,11 @@
  * @brief Compile information from the when statement
  * @param[in] ctx Compile context.
  * @param[in] when_p The parsed when statement structure.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[out] when Pointer where to store pointer to the created compiled when structure.
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_compile_when(struct lysc_ctx *ctx, struct lysp_when *when_p, int options, struct lysc_when **when)
+lys_compile_when(struct lysc_ctx *ctx, struct lysp_when *when_p, struct lysc_when **when)
 {
     unsigned int u;
     LY_ERR ret = LY_SUCCESS;
@@ -539,7 +546,7 @@
     DUP_STRING(ctx->ctx, when_p->dsc, (*when)->dsc);
     DUP_STRING(ctx->ctx, when_p->ref, (*when)->ref);
     LY_CHECK_ERR_GOTO(!(*when)->cond, ret = ly_errcode(ctx->ctx), done);
-    COMPILE_ARRAY_GOTO(ctx, when_p->exts, (*when)->exts, options, u, lys_compile_ext, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, when_p->exts, (*when)->exts, u, lys_compile_ext, ret, done);
 
 done:
     return ret;
@@ -549,12 +556,11 @@
  * @brief Compile information from the must statement
  * @param[in] ctx Compile context.
  * @param[in] must_p The parsed must statement structure.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] must Prepared (empty) compiled must structure to fill.
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_compile_must(struct lysc_ctx *ctx, struct lysp_restr *must_p, int options, struct lysc_must *must)
+lys_compile_must(struct lysc_ctx *ctx, struct lysp_restr *must_p, struct lysc_must *must)
 {
     unsigned int u;
     LY_ERR ret = LY_SUCCESS;
@@ -566,7 +572,7 @@
     DUP_STRING(ctx->ctx, must_p->emsg, must->emsg);
     DUP_STRING(ctx->ctx, must_p->dsc, must->dsc);
     DUP_STRING(ctx->ctx, must_p->ref, must->ref);
-    COMPILE_ARRAY_GOTO(ctx, must_p->exts, must->exts, options, u, lys_compile_ext, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, must_p->exts, must->exts, u, lys_compile_ext, ret, done);
 
 done:
     return ret;
@@ -576,19 +582,18 @@
  * @brief Compile information from the import statement
  * @param[in] ctx Compile context.
  * @param[in] imp_p The parsed import statement structure.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] imp Prepared (empty) compiled import structure to fill.
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_compile_import(struct lysc_ctx *ctx, struct lysp_import *imp_p, int options, struct lysc_import *imp)
+lys_compile_import(struct lysc_ctx *ctx, struct lysp_import *imp_p, struct lysc_import *imp)
 {
     unsigned int u;
     struct lys_module *mod = NULL;
     LY_ERR ret = LY_SUCCESS;
 
     DUP_STRING(ctx->ctx, imp_p->prefix, imp->prefix);
-    COMPILE_ARRAY_GOTO(ctx, imp_p->exts, imp->exts, options, u, lys_compile_ext, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, imp_p->exts, imp->exts, u, lys_compile_ext, ret, done);
     imp->module = imp_p->module;
 
     /* make sure that we have the parsed version (lysp_) of the imported module to import groupings or typedefs.
@@ -625,24 +630,24 @@
  *
  * @param[in] ctx Compile context.
  * @param[in] ident_p The parsed identity statement structure.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in] idents List of so far compiled identities to check the name uniqueness.
  * @param[in,out] ident Prepared (empty) compiled identity structure to fill.
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_compile_identity(struct lysc_ctx *ctx, struct lysp_ident *ident_p, int options, struct lysc_ident *idents, struct lysc_ident *ident)
+lys_compile_identity(struct lysc_ctx *ctx, struct lysp_ident *ident_p, struct lysc_ident *idents, struct lysc_ident *ident)
 {
     unsigned int u;
     LY_ERR ret = LY_SUCCESS;
 
     COMPILE_CHECK_UNIQUENESS(ctx, idents, name, ident, "identity", ident_p->name);
     DUP_STRING(ctx->ctx, ident_p->name, ident->name);
-    DUP_STRING(ctx->ctx, ident_p->ref, ident->dsc);
-    DUP_STRING(ctx->ctx, ident_p->ref, ident->dsc);
-    COMPILE_ARRAY_GOTO(ctx, ident_p->iffeatures, ident->iffeatures, options, u, lys_compile_iffeature, ret, done);
+    DUP_STRING(ctx->ctx, ident_p->dsc, ident->dsc);
+    DUP_STRING(ctx->ctx, ident_p->ref, ident->ref);
+    ident->module = ctx->mod;
+    COMPILE_ARRAY_GOTO(ctx, ident_p->iffeatures, ident->iffeatures, u, lys_compile_iffeature, ret, done);
     /* backlings (derived) can be added no sooner than when all the identities in the current module are present */
-    COMPILE_ARRAY_GOTO(ctx, ident_p->exts, ident->exts, options, u, lys_compile_ext, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, ident_p->exts, ident->exts, u, lys_compile_ext, ret, done);
     ident->flags = ident_p->flags;
 
 done:
@@ -810,7 +815,7 @@
 }
 
 LY_ERR
-lys_feature_precompile(struct ly_ctx *ctx, struct lysp_feature *features_p, struct lysc_feature **features)
+lys_feature_precompile(struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features)
 {
     unsigned int offset = 0, u;
     struct lysc_ctx context = {0};
@@ -833,6 +838,7 @@
         DUP_STRING(ctx, features_p[u].dsc, (*features)[offset + u].dsc);
         DUP_STRING(ctx, features_p[u].ref, (*features)[offset + u].ref);
         (*features)[offset + u].flags = features_p[u].flags;
+        (*features)[offset + u].module = module;
     }
 
     return LY_SUCCESS;
@@ -899,12 +905,11 @@
  *
  * @param[in] ctx Compile context.
  * @param[in] feature_p Parsed feature definition to compile.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] features List of already (pre)compiled features to find the corresponding precompiled feature structure.
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_feature_precompile_finish(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *features)
+lys_feature_precompile_finish(struct lysc_ctx *ctx, struct lysp_feature *feature_p, struct lysc_feature *features)
 {
     unsigned int u, v, x;
     struct lysc_feature *feature, **df;
@@ -918,8 +923,8 @@
         feature = &features[x];
 
         /* finish compilation started in lys_feature_precompile() */
-        COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, options, u, lys_compile_ext, ret, done);
-        COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, options, u, lys_compile_iffeature, ret, done);
+        COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, u, lys_compile_ext, ret, done);
+        COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, u, lys_compile_iffeature, ret, done);
         if (feature->iffeatures) {
             for (u = 0; u < LY_ARRAY_SIZE(feature->iffeatures); ++u) {
                 if (feature->iffeatures[u].features) {
@@ -997,7 +1002,7 @@
  * @param[out] valcopy NULL-terminated string with the numeric value to parse and store.
  * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID (no number) or LY_EINVAL (decimal64 not matching fraction-digits value).
  */
-static LY_ERR
+LY_ERR
 range_part_check_value_syntax(struct lysc_ctx *ctx, LY_DATA_TYPE basetype, uint8_t frdigits, const char *value, size_t *len, char **valcopy)
 {
     size_t fraction = 0, size;
@@ -1499,16 +1504,18 @@
  *
  * @param[in] ctx Compile context.
  * @param[in] pattern Pattern to check.
- * @param[in,out] pcre_precomp Precompiled PCRE pattern. If NULL, the compiled information used to validate pattern are freed.
+ * @param[in,out] pcre2_code Compiled PCRE2 pattern. If NULL, the compiled information used to validate pattern are freed.
  * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID.
  */
 static LY_ERR
-lys_compile_type_pattern_check(struct lysc_ctx *ctx, const char *pattern, pcre **pcre_precomp)
+lys_compile_type_pattern_check(struct lysc_ctx *ctx, const char *pattern, pcre2_code **code)
 {
-    int idx, idx2, start, end, err_offset, count;
+    int idx, idx2, start, end, count;
     char *perl_regex, *ptr;
-    const char *err_msg, *orig_ptr;
-    pcre *precomp;
+    int err_code;
+    const char *orig_ptr;
+    PCRE2_SIZE err_offset;
+    pcre2_code *code_local;
 #define URANGE_LEN 19
     char *ublock2urange[][2] = {
         {"BasicLatin", "[\\x{0000}-\\x{007F}]"},
@@ -1609,12 +1616,6 @@
 
     ptr = perl_regex;
 
-    if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
-        /* we will add line-end anchoring */
-        ptr[0] = '(';
-        ++ptr;
-    }
-
     for (orig_ptr = pattern; orig_ptr[0]; ++orig_ptr) {
         if (orig_ptr[0] == '$') {
             ptr += sprintf(ptr, "\\$");
@@ -1625,13 +1626,8 @@
             ++ptr;
         }
     }
-
-    if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
-        ptr += sprintf(ptr, ")$");
-    } else {
-        ptr[0] = '\0';
-        ++ptr;
-    }
+    ptr[0] = '\0';
+    ++ptr;
 
     /* substitute Unicode Character Blocks with exact Character Ranges */
     while ((ptr = strstr(perl_regex, "\\p{Is"))) {
@@ -1685,19 +1681,22 @@
     }
 
     /* must return 0, already checked during parsing */
-    precomp = pcre_compile(perl_regex, PCRE_UTF8 | PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE,
-                           &err_msg, &err_offset, NULL);
-    if (!precomp) {
+    code_local = pcre2_compile((PCRE2_SPTR)perl_regex, PCRE2_ZERO_TERMINATED,
+                               PCRE2_UTF | PCRE2_ANCHORED | PCRE2_ENDANCHORED | PCRE2_DOLLAR_ENDONLY | PCRE2_NO_AUTO_CAPTURE,
+                           &err_code, &err_offset, NULL);
+    if (!code_local) {
+        PCRE2_UCHAR err_msg[256] = {0};
+        pcre2_get_error_message(err_code, err_msg, 256);
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_INREGEXP, pattern, perl_regex + err_offset, err_msg);
         free(perl_regex);
         return LY_EVALID;
     }
     free(perl_regex);
 
-    if (pcre_precomp) {
-        *pcre_precomp = precomp;
+    if (code) {
+        *code = code_local;
     } else {
-        free(precomp);
+        free(code_local);
     }
 
     return LY_SUCCESS;
@@ -1709,19 +1708,17 @@
  * @brief Compile parsed pattern restriction in conjunction with the patterns from base type.
  * @param[in] ctx Compile context.
  * @param[in] patterns_p Array of parsed patterns from the current type to compile.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in] base_patterns Compiled patterns from the type from which the current type is derived.
  * Patterns from the base type are inherited to have all the patterns that have to match at one place.
  * @param[out] patterns Pointer to the storage for the patterns of the current type.
  * @return LY_ERR LY_SUCCESS, LY_EMEM, LY_EVALID.
  */
 static LY_ERR
-lys_compile_type_patterns(struct lysc_ctx *ctx, struct lysp_restr *patterns_p, int options,
+lys_compile_type_patterns(struct lysc_ctx *ctx, struct lysp_restr *patterns_p,
                           struct lysc_pattern **base_patterns, struct lysc_pattern ***patterns)
 {
     struct lysc_pattern **pattern;
     unsigned int u, v;
-    const char *err_msg;
     LY_ERR ret = LY_SUCCESS;
 
     /* first, copy the patterns from the base type */
@@ -1735,22 +1732,18 @@
         *pattern = calloc(1, sizeof **pattern);
         ++(*pattern)->refcount;
 
-        ret = lys_compile_type_pattern_check(ctx, &patterns_p[u].arg[1], &(*pattern)->expr);
+        ret = lys_compile_type_pattern_check(ctx, &patterns_p[u].arg[1], &(*pattern)->code);
         LY_CHECK_RET(ret);
-        (*pattern)->expr_extra = pcre_study((*pattern)->expr, 0, &err_msg);
-        if (err_msg) {
-            LOGWRN(ctx->ctx, "Studying pattern \"%s\" failed (%s).", pattern, err_msg);
-        }
 
         if (patterns_p[u].arg[0] == 0x15) {
             (*pattern)->inverted = 1;
         }
+        DUP_STRING(ctx->ctx, &patterns_p[u].arg[1], (*pattern)->expr);
         DUP_STRING(ctx->ctx, patterns_p[u].eapptag, (*pattern)->eapptag);
         DUP_STRING(ctx->ctx, patterns_p[u].emsg, (*pattern)->emsg);
         DUP_STRING(ctx->ctx, patterns_p[u].dsc, (*pattern)->dsc);
         DUP_STRING(ctx->ctx, patterns_p[u].ref, (*pattern)->ref);
-        COMPILE_ARRAY_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts,
-                           options, v, lys_compile_ext, ret, done);
+        COMPILE_ARRAY_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts, v, lys_compile_ext, ret, done);
     }
 done:
     return ret;
@@ -1795,20 +1788,19 @@
  * @param[in] ctx Compile context.
  * @param[in] enums_p Array of the parsed enum structures to compile.
  * @param[in] basetype Base YANG built-in type from which the current type is derived. Only LY_TYPE_ENUM and LY_TYPE_BITS are expected.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in] base_enums Array of the compiled enums information from the (latest) base type to check if the current enums are compatible.
  * @param[out] enums Newly created array of the compiled enums information for the current type.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_type_enums(struct lysc_ctx *ctx, struct lysp_type_enum *enums_p, LY_DATA_TYPE basetype, int options,
-                       struct lysc_type_enum_item *base_enums, struct lysc_type_enum_item **enums)
+lys_compile_type_enums(struct lysc_ctx *ctx, struct lysp_type_enum *enums_p, LY_DATA_TYPE basetype,
+                       struct lysc_type_bitenum_item *base_enums, struct lysc_type_bitenum_item **enums)
 {
     LY_ERR ret = LY_SUCCESS;
     unsigned int u, v, match;
     int32_t value = 0;
     uint32_t position = 0;
-    struct lysc_type_enum_item *e, storage;
+    struct lysc_type_bitenum_item *e, storage;
 
     if (base_enums && ctx->mod_def->version < 2) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "%s type can be subtyped only in YANG 1.1 modules.",
@@ -1821,6 +1813,7 @@
         DUP_STRING(ctx->ctx, enums_p[u].name, e->name);
         DUP_STRING(ctx->ctx, enums_p[u].ref, e->dsc);
         DUP_STRING(ctx->ctx, enums_p[u].ref, e->ref);
+        e->flags = enums_p[u].flags & LYS_FLAGS_COMPILED_MASK;
         if (base_enums) {
             /* check the enum/bit presence in the base type - the set of enums/bits in the derived type must be a subset */
             LY_ARRAY_FOR(base_enums, v) {
@@ -1838,6 +1831,7 @@
         }
 
         if (basetype == LY_TYPE_ENUM) {
+            e->flags |= LYS_ISENUM;
             if (enums_p[u].flags & LYS_SET_VALUE) {
                 e->value = (int32_t)enums_p[u].value;
                 if (!u || e->value >= value) {
@@ -1919,8 +1913,8 @@
             }
         }
 
-        COMPILE_ARRAY_GOTO(ctx, enums_p[u].iffeatures, e->iffeatures, options, v, lys_compile_iffeature, ret, done);
-        COMPILE_ARRAY_GOTO(ctx, enums_p[u].exts, e->exts, options, v, lys_compile_ext, ret, done);
+        COMPILE_ARRAY_GOTO(ctx, enums_p[u].iffeatures, e->iffeatures, v, lys_compile_iffeature, ret, done);
+        COMPILE_ARRAY_GOTO(ctx, enums_p[u].exts, e->exts, v, lys_compile_ext, ret, done);
 
         if (basetype == LY_TYPE_BITS) {
             /* keep bits ordered by position */
@@ -2017,6 +2011,10 @@
                        *predicate - start, start, src_prefix_len, src_prefix, path_context->name);
                 goto cleanup;
             }
+            if (!mod->implemented) {
+                /* make the module implemented */
+                ly_ctx_module_implement_internal(ctx->ctx, (struct lys_module*)mod, 2);
+            }
         } else {
             mod = start_node->module;
         }
@@ -2127,22 +2125,26 @@
             }
             if (!mod) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                       "Invalid leafref path predicate \"%.*s\" - unable to find module of the node \"%.*s\" in rel-path_keyexpr.",
+                       "Invalid leafref path predicate \"%.*s\" - unable to find module of the node \"%.*s\" in rel-path-keyexpr.",
                        *predicate - start, start, dst_len, dst);
                 goto cleanup;
             }
+            if (!mod->implemented) {
+                /* make the module implemented */
+                ly_ctx_module_implement_internal(ctx->ctx, (struct lys_module*)mod, 2);
+            }
 
             dst_node = lys_child(dst_node, mod, dst, dst_len, 0, LYS_GETNEXT_NOSTATECHECK);
             if (!dst_node) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                       "Invalid leafref path predicate \"%.*s\" - unable to find node \"%.*s\" in the rel-path_keyexpr.",
+                       "Invalid leafref path predicate \"%.*s\" - unable to find node \"%.*s\" in the rel-path-keyexpr.",
                        *predicate - start, start, path_key_expr - pke_start, pke_start);
                 goto cleanup;
             }
         }
         if (!(dst_node->nodetype & (dst_node->module->version < LYS_VERSION_1_1 ? LYS_LEAF : LYS_LEAF | LYS_LEAFLIST))) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                   "Invalid leafref path predicate \"%.*s\" - rel-path_keyexpr \"%.*s\" refers %s instead of leaf.",
+                   "Invalid leafref path predicate \"%.*s\" - rel-path-keyexpr \"%.*s\" refers %s instead of leaf.",
                    *predicate - start, start, path_key_expr - pke_start, pke_start, lys_nodetype2str(dst_node->nodetype));
             goto cleanup;
         }
@@ -2172,7 +2174,7 @@
  * @param[out] has_predicate Flag to mark whether there is a predicate specified.
  * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the path.
  */
-static LY_ERR
+LY_ERR
 lys_path_token(const char **path, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
                int *parent_times, int *has_predicate)
 {
@@ -2412,7 +2414,7 @@
 }
 
 static LY_ERR lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
-                               struct lysp_type *type_p, int options, struct lysc_type **type, const char **units);
+                               struct lysp_type *type_p, struct lysc_type **type, const char **units);
 /**
  * @brief The core of the lys_compile_type() - compile information about the given type (from typedef or leaf/leaf-list).
  * @param[in] ctx Compile context.
@@ -2423,7 +2425,6 @@
  * @param[in] type_p Parsed type to compile.
  * @param[in] module Context module for the leafref path (to correctly resolve prefixes in path)
  * @param[in] basetype Base YANG built-in type of the type to compile.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in] tpdfname Name of the type's typedef, serves as a flag - if it is leaf/leaf-list's type, it is NULL.
  * @param[in] base The latest base (compiled) type from which the current type is being derived.
  * @param[out] type Newly created type structure with the filled information about the type.
@@ -2431,7 +2432,7 @@
  */
 static LY_ERR
 lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
-                  struct lysp_type *type_p, struct lys_module *module, LY_DATA_TYPE basetype, int options, const char *tpdfname,
+                  struct lysp_type *type_p, struct lys_module *module, LY_DATA_TYPE basetype, const char *tpdfname,
                   struct lysc_type *base,  struct lysc_type **type)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -2452,12 +2453,10 @@
 
         /* RFC 7950 9.8.1, 9.4.4 - length, number of octets it contains */
         if (type_p->length) {
-            ret = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
-                                         base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+                                                base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length));
             if (!tpdfname) {
-                COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, bin->length->exts,
-                                   options, u, lys_compile_ext, ret, done);
+                COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, bin->length->exts, u, lys_compile_ext, ret, done);
             }
         }
 
@@ -2470,10 +2469,9 @@
         /* RFC 7950 9.7 - bits */
         bits = (struct lysc_type_bits*)(*type);
         if (type_p->bits) {
-            ret = lys_compile_type_enums(ctx, type_p->bits, basetype, options,
-                                         base ? (struct lysc_type_enum_item*)((struct lysc_type_bits*)base)->bits : NULL,
-                                         (struct lysc_type_enum_item**)&bits->bits);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(lys_compile_type_enums(ctx, type_p->bits, basetype,
+                                                base ? (struct lysc_type_bitenum_item*)((struct lysc_type_bits*)base)->bits : NULL,
+                                                (struct lysc_type_bitenum_item**)&bits->bits));
         }
 
         if (!base && !type_p->flags) {
@@ -2526,12 +2524,10 @@
 
         /* RFC 7950 9.2.4 - range */
         if (type_p->range) {
-            ret = lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
-                                         base ? ((struct lysc_type_dec*)base)->range : NULL, &dec->range);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
+                                                base ? ((struct lysc_type_dec*)base)->range : NULL, &dec->range));
             if (!tpdfname) {
-                COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, dec->range->exts,
-                                   options, u, lys_compile_ext, ret, done);
+                COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, dec->range->exts, u, lys_compile_ext, ret, done);
             }
         }
 
@@ -2545,12 +2541,10 @@
 
         /* RFC 7950 9.4.4 - length */
         if (type_p->length) {
-            ret = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
-                                         base ? ((struct lysc_type_str*)base)->length : NULL, &str->length);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+                                                base ? ((struct lysc_type_str*)base)->length : NULL, &str->length));
             if (!tpdfname) {
-                COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, str->length->exts,
-                                   options, u, lys_compile_ext, ret, done);
+                COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, str->length->exts, u, lys_compile_ext, ret, done);
             }
         } else if (base && ((struct lysc_type_str*)base)->length) {
             str->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_str*)base)->length);
@@ -2558,9 +2552,8 @@
 
         /* RFC 7950 9.4.5 - pattern */
         if (type_p->patterns) {
-            ret = lys_compile_type_patterns(ctx, type_p->patterns, options,
-                                            base ? ((struct lysc_type_str*)base)->patterns : NULL, &str->patterns);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(lys_compile_type_patterns(ctx, type_p->patterns,
+                                                   base ? ((struct lysc_type_str*)base)->patterns : NULL, &str->patterns));
         } else if (base && ((struct lysc_type_str*)base)->patterns) {
             str->patterns = lysc_patterns_dup(ctx->ctx, ((struct lysc_type_str*)base)->patterns);
         }
@@ -2575,9 +2568,8 @@
 
         /* RFC 7950 9.6 - enum */
         if (type_p->enums) {
-            ret = lys_compile_type_enums(ctx, type_p->enums, basetype, options,
-                                         base ? ((struct lysc_type_enum*)base)->enums : NULL, &enumeration->enums);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(lys_compile_type_enums(ctx, type_p->enums, basetype,
+                                                base ? ((struct lysc_type_enum*)base)->enums : NULL, &enumeration->enums));
         }
 
         if (!base && !type_p->flags) {
@@ -2609,12 +2601,10 @@
 
         /* RFC 6020 9.2.4 - range */
         if (type_p->range) {
-            ret = lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
-                                         base ? ((struct lysc_type_num*)base)->range : NULL, &num->range);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
+                                                base ? ((struct lysc_type_num*)base)->range : NULL, &num->range));
             if (!tpdfname) {
-                COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, num->range->exts,
-                                   options, u, lys_compile_ext, ret, done);
+                COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, num->range->exts, u, lys_compile_ext, ret, done);
             }
         }
 
@@ -2642,8 +2632,7 @@
                 }
                 return LY_EVALID;
             }
-            ret = lys_compile_identity_bases(ctx, type_p->bases, NULL, &idref->bases);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->bases, NULL, &idref->bases));
         }
 
         if (!base && !type_p->flags) {
@@ -2743,7 +2732,7 @@
             additional = 0;
             LY_ARRAY_CREATE_RET(ctx->ctx, un->types, LY_ARRAY_SIZE(type_p->types), LY_EVALID);
             for (u = 0; u < LY_ARRAY_SIZE(type_p->types); ++u) {
-                ret = lys_compile_type(ctx, context_node_p, context_flags, context_mod, context_name, &type_p->types[u], options, &un->types[u + additional], NULL);
+                LY_CHECK_RET(lys_compile_type(ctx, context_node_p, context_flags, context_mod, context_name, &type_p->types[u], &un->types[u + additional], NULL));
                 if (un->types[u + additional]->basetype == LY_TYPE_UNION) {
                     /* add space for additional types from the union subtype */
                     un_aux = (struct lysc_type_union *)un->types[u + additional];
@@ -2779,7 +2768,6 @@
                 } else {
                     LY_ARRAY_INCREMENT(un->types);
                 }
-                LY_CHECK_RET(ret);
             }
         }
 
@@ -2818,14 +2806,13 @@
  * @param[in] context_mod Module of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
  * @param[in] context_name Name of the context node or referencing typedef for logging.
  * @param[in] type_p Parsed type to compile.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[out] type Newly created (or reused with increased refcount) type structure with the filled information about the type.
  * @param[out] units Storage for inheriting units value from the typedefs the current type derives from.
  * @return LY_ERR value.
  */
 static LY_ERR
 lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
-                 struct lysp_type *type_p, int options, struct lysc_type **type, const char **units)
+                 struct lysp_type *type_p, struct lysc_type **type, const char **units)
 {
     LY_ERR ret = LY_SUCCESS;
     unsigned int u;
@@ -2834,7 +2821,7 @@
         const struct lysp_tpdf *tpdf;
         struct lysp_node *node;
         struct lysp_module *mod;
-    } *tctx, *tctx_prev = NULL;
+    } *tctx, *tctx_prev = NULL, *tctx_iter;
     LY_DATA_TYPE basetype = LY_TYPE_UNKNOWN;
     struct lysc_type *base = NULL, *prev_type;
     struct ly_set tpdf_chain = {0};
@@ -2885,6 +2872,30 @@
             }
         }
 
+        /* circular typedef reference detection */
+        for (u = 0; u < tpdf_chain.count; u++) {
+            /* local part */
+            tctx_iter = (struct type_context*)tpdf_chain.objs[u];
+            if (tctx_iter->mod == tctx->mod && tctx_iter->node == tctx->node && tctx_iter->tpdf == tctx->tpdf) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid \"%s\" type reference - circular chain of types detected.", tctx->tpdf->name);
+                free(tctx);
+                ret = LY_EVALID;
+                goto cleanup;
+            }
+        }
+        for (u = 0; u < ctx->tpdf_chain.count; u++) {
+            /* global part for unions corner case */
+            tctx_iter = (struct type_context*)ctx->tpdf_chain.objs[u];
+            if (tctx_iter->mod == tctx->mod && tctx_iter->node == tctx->node && tctx_iter->tpdf == tctx->tpdf) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid \"%s\" type reference - circular chain of types detected.", tctx->tpdf->name);
+                free(tctx);
+                ret = LY_EVALID;
+                goto cleanup;
+            }
+        }
+
         /* store information for the following processing */
         ly_set_add(&tpdf_chain, tctx, LY_SET_OPT_USEASLIST);
 
@@ -2958,6 +2969,10 @@
     /* get restrictions from the referred typedefs */
     for (u = tpdf_chain.count - 1; u + 1 > 0; --u) {
         tctx = (struct type_context*)tpdf_chain.objs[u];
+
+        /* remember the typedef context for circular check */
+        ly_set_add(&ctx->tpdf_chain, tctx, LY_SET_OPT_USEASLIST);
+
         if (tctx->tpdf->type.compiled) {
             base = tctx->tpdf->type.compiled;
             continue;
@@ -2983,20 +2998,26 @@
         }
 
         (*type)->basetype = basetype;
+        /* TODO user type plugins */
+        (*type)->plugin = &ly_builtin_type_plugins[basetype];
         prev_type = *type;
         ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->mod, tctx->tpdf->name, &((struct lysp_tpdf*)tctx->tpdf)->type,
                                 basetype & (LY_TYPE_LEAFREF | LY_TYPE_UNION) ? lysp_find_module(ctx->ctx, tctx->mod) : NULL,
-                                basetype, options, tctx->tpdf->name, base, type);
+                                basetype, tctx->tpdf->name, base, type);
         LY_CHECK_GOTO(ret, cleanup);
         base = prev_type;
     }
+    /* remove the processed typedef contexts from the stack for circular check */
+    ctx->tpdf_chain.count = ctx->tpdf_chain.count - tpdf_chain.count;
 
     /* process the type definition in leaf */
     if (type_p->flags || !base || basetype == LY_TYPE_LEAFREF) {
         /* get restrictions from the node itself */
         (*type)->basetype = basetype;
+        /* TODO user type plugins */
+        (*type)->plugin = &ly_builtin_type_plugins[basetype];
         ++(*type)->refcount;
-        ret = lys_compile_type_(ctx, context_node_p, context_flags, context_mod, context_name, type_p, ctx->mod_def, basetype, options, NULL, base, type);
+        ret = lys_compile_type_(ctx, context_node_p, context_flags, context_mod, context_name, type_p, ctx->mod_def, basetype, NULL, base, type);
         LY_CHECK_GOTO(ret, cleanup);
     } else {
         /* no specific restriction in leaf's type definition, copy from the base */
@@ -3008,7 +3029,7 @@
         DUP_STRING(ctx->ctx, dflt, (*type)->dflt);
     }
 
-    COMPILE_ARRAY_GOTO(ctx, type_p->exts, (*type)->exts, options, u, lys_compile_ext, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, type_p->exts, (*type)->exts, u, lys_compile_ext, ret, cleanup);
 
 cleanup:
     ly_set_erase(&tpdf_chain, free);
@@ -3106,25 +3127,26 @@
     return LY_EEXIST;
 }
 
-static LY_ERR lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent, uint16_t uses_status);
+static LY_ERR lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node *parent, uint16_t uses_status);
 
 /**
  * @brief Compile parsed RPC/action schema node information.
  * @param[in] ctx Compile context
- * @param[in] node_p Parsed RPC/action schema node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] action_p Parsed RPC/action schema node.
+ * @param[in] parent Parent node of the action, NULL in case of RPC (top-level action)
  * @param[in,out] action Prepared (empty) compiled action structure to fill.
  * @param[in] uses_status If the RPC/action is being placed instead of uses, here we have the uses's status value (as node's flags).
  * Zero means no uses, non-zero value with no status bit set mean the default status.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_action(struct lysc_ctx *ctx, struct lysp_action *action_p, int options,
+lys_compile_action(struct lysc_ctx *ctx, struct lysp_action *action_p,
                    struct lysc_node *parent, struct lysc_action *action, uint16_t uses_status)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysp_node *child_p;
     unsigned int u;
+    int opt_prev = ctx->options;
 
     if (lys_compile_node_uniqness(ctx, parent ? lysc_node_children(parent, 0) : ctx->mod->compiled->data,
                                   parent ? lysc_node_actions(parent) : ctx->mod->compiled->rpcs,
@@ -3133,16 +3155,17 @@
         return LY_EVALID;
     }
 
-    if (options & LYSC_OPT_RPC_MASK) {
+    if (ctx->options & (LYSC_OPT_RPC_MASK | LYSC_OPT_NOTIFICATION)) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-               "Action \"%s\" is placed inside another RPC/action.", action_p->name);
+               "Action \"%s\" is placed inside %s.", action_p->name,
+               ctx->options & LYSC_OPT_RPC_MASK ? "another RPC/action" : "Notification");
         return LY_EVALID;
     }
 
     action->nodetype = LYS_ACTION;
     action->module = ctx->mod;
     action->parent = parent;
-    if (!(options & LYSC_OPT_FREE_SP)) {
+    if (!(ctx->options & LYSC_OPT_FREE_SP)) {
         action->sp = action_p;
     }
     action->flags = action_p->flags & LYS_FLAGS_COMPILED_MASK;
@@ -3154,24 +3177,90 @@
     DUP_STRING(ctx->ctx, action_p->name, action->name);
     DUP_STRING(ctx->ctx, action_p->dsc, action->dsc);
     DUP_STRING(ctx->ctx, action_p->ref, action->ref);
-    COMPILE_ARRAY_GOTO(ctx, action_p->iffeatures, action->iffeatures, options, u, lys_compile_iffeature, ret, cleanup);
-    COMPILE_ARRAY_GOTO(ctx, action_p->exts, action->exts, options, u, lys_compile_ext, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, action_p->iffeatures, action->iffeatures, u, lys_compile_iffeature, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, action_p->exts, action->exts, u, lys_compile_ext, ret, cleanup);
 
     /* input */
-    COMPILE_ARRAY_GOTO(ctx, action_p->input.musts, action->input.musts, options, u, lys_compile_must, ret, cleanup);
-    COMPILE_ARRAY_GOTO(ctx, action_p->input.exts, action->input.exts, options, u, lys_compile_ext, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, action_p->input.musts, action->input.musts, u, lys_compile_must, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, action_p->input.exts, action->input_exts, u, lys_compile_ext, ret, cleanup);
+    ctx->options |= LYSC_OPT_RPC_INPUT;
     LY_LIST_FOR(action_p->input.data, child_p) {
-        LY_CHECK_RET(lys_compile_node(ctx, child_p, options | LYSC_OPT_RPC_INPUT, (struct lysc_node*)action, uses_status));
+        LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)action, uses_status));
     }
+    ctx->options = opt_prev;
 
     /* output */
-    COMPILE_ARRAY_GOTO(ctx, action_p->output.musts, action->output.musts, options, u, lys_compile_must, ret, cleanup);
-    COMPILE_ARRAY_GOTO(ctx, action_p->output.exts, action->output.exts, options, u, lys_compile_ext, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, action_p->output.musts, action->output.musts, u, lys_compile_must, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, action_p->output.exts, action->output_exts, u, lys_compile_ext, ret, cleanup);
+    ctx->options |= LYSC_OPT_RPC_OUTPUT;
     LY_LIST_FOR(action_p->output.data, child_p) {
-        LY_CHECK_RET(lys_compile_node(ctx, child_p, options | LYSC_OPT_RPC_OUTPUT, (struct lysc_node*)action, uses_status));
+        LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)action, uses_status));
     }
 
 cleanup:
+    ctx->options = opt_prev;
+    return ret;
+}
+
+/**
+ * @brief Compile parsed Notification schema node information.
+ * @param[in] ctx Compile context
+ * @param[in] notif_p Parsed Notification schema node.
+ * @param[in] parent Parent node of the Notification, NULL in case of top-level Notification
+ * @param[in,out] notif Prepared (empty) compiled notification structure to fill.
+ * @param[in] uses_status If the Notification is being placed instead of uses, here we have the uses's status value (as node's flags).
+ * Zero means no uses, non-zero value with no status bit set mean the default status.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_notif(struct lysc_ctx *ctx, struct lysp_notif *notif_p,
+                  struct lysc_node *parent, struct lysc_notif *notif, uint16_t uses_status)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysp_node *child_p;
+    unsigned int u;
+    int opt_prev = ctx->options;
+
+    if (lys_compile_node_uniqness(ctx, parent ? lysc_node_children(parent, 0) : ctx->mod->compiled->data,
+                                  parent ? lysc_node_actions(parent) : ctx->mod->compiled->rpcs,
+                                  parent ? lysc_node_notifs(parent) : ctx->mod->compiled->notifs,
+                                  notif_p->name, notif)) {
+        return LY_EVALID;
+    }
+
+    if (ctx->options & (LYSC_OPT_RPC_MASK | LYSC_OPT_NOTIFICATION)) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+               "Notification \"%s\" is placed inside %s.", notif_p->name,
+               ctx->options & LYSC_OPT_RPC_MASK ? "RPC/action" : "another Notification");
+        return LY_EVALID;
+    }
+
+    notif->nodetype = LYS_NOTIF;
+    notif->module = ctx->mod;
+    notif->parent = parent;
+    if (!(ctx->options & LYSC_OPT_FREE_SP)) {
+        notif->sp = notif_p;
+    }
+    notif->flags = notif_p->flags & LYS_FLAGS_COMPILED_MASK;
+
+    /* status - it is not inherited by specification, but it does not make sense to have
+     * current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
+    LY_CHECK_RET(lys_compile_status(ctx, &notif->flags, uses_status ? uses_status : (parent ? parent->flags : 0)));
+
+    DUP_STRING(ctx->ctx, notif_p->name, notif->name);
+    DUP_STRING(ctx->ctx, notif_p->dsc, notif->dsc);
+    DUP_STRING(ctx->ctx, notif_p->ref, notif->ref);
+    COMPILE_ARRAY_GOTO(ctx, notif_p->iffeatures, notif->iffeatures, u, lys_compile_iffeature, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, notif_p->exts, notif->exts, u, lys_compile_ext, ret, cleanup);
+    COMPILE_ARRAY_GOTO(ctx, notif_p->musts, notif->musts, u, lys_compile_must, ret, cleanup);
+
+    ctx->options |= LYSC_OPT_NOTIFICATION;
+    LY_LIST_FOR(notif_p->data, child_p) {
+        LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)notif, uses_status));
+    }
+
+cleanup:
+    ctx->options = opt_prev;
     return ret;
 }
 
@@ -3179,13 +3268,12 @@
  * @brief Compile parsed container node information.
  * @param[in] ctx Compile context
  * @param[in] node_p Parsed container node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
  * is enriched with the container-specific information.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_node_container(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+lys_compile_node_container(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node *node)
 {
     struct lysp_node_container *cont_p = (struct lysp_node_container*)node_p;
     struct lysc_node_container *cont = (struct lysc_node_container*)node;
@@ -3198,12 +3286,12 @@
     }
 
     LY_LIST_FOR(cont_p->child, child_p) {
-        LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node, 0));
+        LY_CHECK_RET(lys_compile_node(ctx, child_p, node, 0));
     }
 
-    COMPILE_ARRAY_GOTO(ctx, cont_p->musts, cont->musts, options, u, lys_compile_must, ret, done);
-    COMPILE_ARRAY1_GOTO(ctx, cont_p->actions, cont->actions, node, options, u, lys_compile_action, 0, ret, done);
-    // TODO COMPILE_ARRAY1_GOTO(ctx, cont_p->notifs, cont->notifs, node, options, u, lys_compile_notif, 0, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, cont_p->musts, cont->musts, u, lys_compile_must, ret, done);
+    COMPILE_ARRAY1_GOTO(ctx, cont_p->actions, cont->actions, node, u, lys_compile_action, 0, ret, done);
+    COMPILE_ARRAY1_GOTO(ctx, cont_p->notifs, cont->notifs, node, u, lys_compile_notif, 0, ret, done);
 
 done:
     return ret;
@@ -3214,17 +3302,16 @@
  * @param[in] ctx Compile context.
  * @param[in] context_node Schema node where the type/typedef is placed to correctly find the base types.
  * @param[in] type_p Parsed type to compile.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] leaf Compiled leaf structure (possibly cast leaf-list) to provide node information and to store the compiled type information.
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_compile_node_type(struct lysc_ctx *ctx, struct lysp_node *context_node, struct lysp_type *type_p, int options, struct lysc_node_leaf *leaf)
+lys_compile_node_type(struct lysc_ctx *ctx, struct lysp_node *context_node, struct lysp_type *type_p, struct lysc_node_leaf *leaf)
 {
     unsigned int u, v;
     struct lysc_node_leaflist *llist = (struct lysc_node_leaflist*)leaf;
 
-    LY_CHECK_RET(lys_compile_type(ctx, context_node, leaf->flags, ctx->mod_def->parsed, leaf->name, type_p, options, &leaf->type,
+    LY_CHECK_RET(lys_compile_type(ctx, context_node, leaf->flags, ctx->mod_def->parsed, leaf->name, type_p, &leaf->type,
                                   leaf->units ? NULL : &leaf->units));
     if (leaf->nodetype == LYS_LEAFLIST) {
         if (llist->type->dflt && !llist->dflts && !llist->min) {
@@ -3282,20 +3369,19 @@
  * @brief Compile parsed leaf node information.
  * @param[in] ctx Compile context
  * @param[in] node_p Parsed leaf node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
  * is enriched with the leaf-specific information.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_node_leaf(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+lys_compile_node_leaf(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node *node)
 {
     struct lysp_node_leaf *leaf_p = (struct lysp_node_leaf*)node_p;
     struct lysc_node_leaf *leaf = (struct lysc_node_leaf*)node;
     unsigned int u;
     LY_ERR ret = LY_SUCCESS;
 
-    COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, options, u, lys_compile_must, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, u, lys_compile_must, ret, done);
     if (leaf_p->units) {
         leaf->units = lydict_insert(ctx->ctx, leaf_p->units, 0);
         leaf->flags |= LYS_SET_UNITS;
@@ -3305,7 +3391,7 @@
         leaf->flags |= LYS_SET_DFLT;
     }
 
-    ret = lys_compile_node_type(ctx, node_p, &leaf_p->type, options, leaf);
+    ret = lys_compile_node_type(ctx, node_p, &leaf_p->type, leaf);
 
 done:
     return ret;
@@ -3315,20 +3401,19 @@
  * @brief Compile parsed leaf-list node information.
  * @param[in] ctx Compile context
  * @param[in] node_p Parsed leaf-list node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
  * is enriched with the leaf-list-specific information.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_node_leaflist(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+lys_compile_node_leaflist(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node *node)
 {
     struct lysp_node_leaflist *llist_p = (struct lysp_node_leaflist*)node_p;
     struct lysc_node_leaflist *llist = (struct lysc_node_leaflist*)node;
     unsigned int u;
     LY_ERR ret = LY_SUCCESS;
 
-    COMPILE_ARRAY_GOTO(ctx, llist_p->musts, llist->musts, options, u, lys_compile_must, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, llist_p->musts, llist->musts, u, lys_compile_must, ret, done);
     if (llist_p->units) {
         llist->units = lydict_insert(ctx->ctx, llist_p->units, 0);
         llist->flags |= LYS_SET_UNITS;
@@ -3349,7 +3434,7 @@
     }
     llist->max = llist_p->max ? llist_p->max : (uint32_t)-1;
 
-    ret = lys_compile_node_type(ctx, node_p, &llist_p->type, options, (struct lysc_node_leaf*)llist);
+    ret = lys_compile_node_type(ctx, node_p, &llist_p->type, (struct lysc_node_leaf*)llist);
 
 done:
     return ret;
@@ -3439,13 +3524,12 @@
  * @brief Compile parsed list node information.
  * @param[in] ctx Compile context
  * @param[in] node_p Parsed list node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
  * is enriched with the list-specific information.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node *node)
 {
     struct lysp_node_list *list_p = (struct lysp_node_list*)node_p;
     struct lysc_node_list *list = (struct lysc_node_list*)node;
@@ -3463,10 +3547,10 @@
     list->max = list_p->max ? list_p->max : (uint32_t)-1;
 
     LY_LIST_FOR(list_p->child, child_p) {
-        LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node, 0));
+        LY_CHECK_RET(lys_compile_node(ctx, child_p, node, 0));
     }
 
-    COMPILE_ARRAY_GOTO(ctx, list_p->musts, list->musts, options, u, lys_compile_must, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, list_p->musts, list->musts, u, lys_compile_must, ret, done);
 
     /* keys */
     if ((list->flags & LYS_CONFIG_W) && (!list_p->key || !list_p->key[0])) {
@@ -3550,8 +3634,8 @@
         LY_CHECK_RET(lys_compile_node_list_unique(ctx, list->module, list_p->uniques, list));
     }
 
-    COMPILE_ARRAY1_GOTO(ctx, list_p->actions, list->actions, node, options, u, lys_compile_action, 0, ret, done);
-    // TODO COMPILE_ARRAY1_GOTO(ctx, list_p->notifs, list->notifs, node, options, u, lys_compile_notif, 0, ret, done);
+    COMPILE_ARRAY1_GOTO(ctx, list_p->actions, list->actions, node, u, lys_compile_action, 0, ret, done);
+    COMPILE_ARRAY1_GOTO(ctx, list_p->notifs, list->notifs, node, u, lys_compile_notif, 0, ret, done);
 
 done:
     return ret;
@@ -3675,27 +3759,25 @@
  * @brief Compile parsed choice node information.
  * @param[in] ctx Compile context
  * @param[in] node_p Parsed choice node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
  * is enriched with the choice-specific information.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_node_choice(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+lys_compile_node_choice(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node *node)
 {
     struct lysp_node_choice *ch_p = (struct lysp_node_choice*)node_p;
     struct lysc_node_choice *ch = (struct lysc_node_choice*)node;
     struct lysp_node *child_p, *case_child_p;
-    struct lys_module;
     LY_ERR ret = LY_SUCCESS;
 
     LY_LIST_FOR(ch_p->child, child_p) {
         if (child_p->nodetype == LYS_CASE) {
             LY_LIST_FOR(((struct lysp_node_case*)child_p)->child, case_child_p) {
-                LY_CHECK_RET(lys_compile_node(ctx, case_child_p, options, node, 0));
+                LY_CHECK_RET(lys_compile_node(ctx, case_child_p, node, 0));
             }
         } else {
-            LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node, 0));
+            LY_CHECK_RET(lys_compile_node(ctx, child_p, node, 0));
         }
     }
 
@@ -3711,20 +3793,19 @@
  * @brief Compile parsed anydata or anyxml node information.
  * @param[in] ctx Compile context
  * @param[in] node_p Parsed anydata or anyxml node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
  * is enriched with the any-specific information.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_node_any(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+lys_compile_node_any(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node *node)
 {
     struct lysp_node_anydata *any_p = (struct lysp_node_anydata*)node_p;
     struct lysc_node_anydata *any = (struct lysc_node_anydata*)node;
     unsigned int u;
     LY_ERR ret = LY_SUCCESS;
 
-    COMPILE_ARRAY_GOTO(ctx, any_p->musts, any->musts, options, u, lys_compile_must, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, any_p->musts, any->musts, u, lys_compile_must, ret, done);
 
     if (any->flags & LYS_CONFIG_W) {
         LOGWRN(ctx->ctx, "Use of %s to define configuration data is not recommended.",
@@ -3741,18 +3822,17 @@
  * @param[in] parent Parent node holding the children list, in case of node from a choice's case,
  * the choice itself is expected instead of a specific case node.
  * @param[in] node Schema node to connect into the list.
- * @param[in] options Compile options to distinguish input/output when placing node into RPC/action.
  * @return LY_ERR value - LY_SUCCESS or LY_EEXIST.
  */
 static LY_ERR
-lys_compile_node_connect(struct lysc_ctx *ctx, struct lysc_node *parent, struct lysc_node *node, int options)
+lys_compile_node_connect(struct lysc_ctx *ctx, struct lysc_node *parent, struct lysc_node *node)
 {
     struct lysc_node **children;
 
     if (node->nodetype == LYS_CASE) {
         children = (struct lysc_node**)&((struct lysc_node_choice*)parent)->cases;
     } else {
-        children = lysc_node_children_p(parent, options);
+        children = lysc_node_children_p(parent, ctx->options);
     }
     if (children) {
         if (!(*children)) {
@@ -3802,17 +3882,16 @@
  * @param[in] ctx Compile context.
  * @param[in] node_p Node image from the parsed tree. If the case is explicit, it is the LYS_CASE node, but in case of implicit case,
  *                   it is the LYS_CHOICE node or LYS_AUGMENT node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in] ch The compiled choice structure where the new case structures are created (if needed).
  * @param[in] child The new data node being part of a case (no matter if explicit or implicit).
  * @return The case structure where the child node belongs to, NULL in case of error. Note that the child is not connected into the siblings list,
  * it is linked from the case structure only in case it is its first child.
  */
 static struct lysc_node_case*
-lys_compile_node_case(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node_choice *ch, struct lysc_node *child)
+lys_compile_node_case(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node_choice *ch, struct lysc_node *child)
 {
     struct lysc_node *iter;
-    struct lysc_node_case *cs;
+    struct lysc_node_case *cs = NULL;
     struct lysc_when **when;
     unsigned int u;
     LY_ERR ret;
@@ -3846,15 +3925,15 @@
         cs->sp = node_p;
 
         /* check the case's status (don't need to solve uses_status since case statement cannot be directly in grouping statement */
-        LY_CHECK_RET(lys_compile_status(ctx, &cs->flags, ch->flags), NULL);
+        LY_CHECK_GOTO(lys_compile_status(ctx, &cs->flags, ch->flags), error);
 
         if (node_p->when) {
             LY_ARRAY_NEW_GOTO(ctx->ctx, cs->when, when, ret, error);
-            ret = lys_compile_when(ctx, node_p->when, options, when);
+            ret = lys_compile_when(ctx, node_p->when, when);
             LY_CHECK_GOTO(ret, error);
             (*when)->context = lysc_xpath_context(ch->parent);
         }
-        COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, cs->iffeatures, options, u, lys_compile_iffeature, ret, error);
+        COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, cs->iffeatures, u, lys_compile_iffeature, ret, error);
     } else {
         LOGINT(ctx->ctx);
         goto error;
@@ -3862,12 +3941,15 @@
     cs->module = ctx->mod;
     cs->prev = (struct lysc_node*)cs;
     cs->nodetype = LYS_CASE;
-    lys_compile_node_connect(ctx, (struct lysc_node*)ch, (struct lysc_node*)cs, options);
+    lys_compile_node_connect(ctx, (struct lysc_node*)ch, (struct lysc_node*)cs);
     cs->parent = (struct lysc_node*)ch;
     cs->child = child;
 
     return cs;
 error:
+    if (cs) {
+        lysc_node_free(ctx->ctx, (struct lysc_node*)cs);
+    }
     return NULL;
 
 #undef UNIQUE_CHECK
@@ -4055,14 +4137,13 @@
  *
  * @param[in] ctx Compile context.
  * @param[in] aug_p Parsed augment to compile.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in] parent Parent node to provide the augment's context. It is NULL for the top level augments and a node holding uses's
  * children in case of the augmenting uses data.
  * @return LY_SUCCESS on success.
  * @return LY_EVALID on failure.
  */
 LY_ERR
-lys_compile_augment(struct lysc_ctx *ctx, struct lysp_augment *aug_p, int options, const struct lysc_node *parent)
+lys_compile_augment(struct lysc_ctx *ctx, struct lysp_augment *aug_p, const struct lysc_node *parent)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysp_node *node_p, *case_node_p;
@@ -4072,6 +4153,7 @@
     int allow_mandatory = 0;
     uint16_t flags = 0;
     unsigned int u;
+    int opt_prev = ctx->options;
 
     ret = lys_resolve_schema_nodeid(ctx, aug_p->nodeid, 0, parent, parent ? parent->module : ctx->mod_def,
                                                LYS_CONTAINER | LYS_LIST | LYS_CHOICE | LYS_CASE | LYS_INOUT | LYS_NOTIF,
@@ -4107,13 +4189,15 @@
         }
 
         /* compile the children */
+        ctx->options |= flags;
         if (node_p->nodetype != LYS_CASE) {
-            LY_CHECK_RET(lys_compile_node(ctx, node_p, options | flags, target, 0));
+            LY_CHECK_RET(lys_compile_node(ctx, node_p, target, 0));
         } else {
             LY_LIST_FOR(((struct lysp_node_case *)node_p)->child, case_node_p) {
-                LY_CHECK_RET(lys_compile_node(ctx, case_node_p, options | flags, target, 0));
+                LY_CHECK_RET(lys_compile_node(ctx, case_node_p, target, 0));
             }
         }
+        ctx->options = opt_prev;
 
         /* since the augment node is not present in the compiled tree, we need to pass some of its statements to all its children,
          * here we gets the last created node as last children of our parent */
@@ -4141,7 +4225,7 @@
         if (aug_p->when) {
             LY_ARRAY_NEW_GOTO(ctx->ctx, node->when, when, ret, error);
             if (!when_shared) {
-                ret = lys_compile_when(ctx, aug_p->when, options, when);
+                ret = lys_compile_when(ctx, aug_p->when, when);
                 LY_CHECK_GOTO(ret, error);
                 (*when)->context = lysc_xpath_context(target);
                 when_shared = *when;
@@ -4152,18 +4236,22 @@
         }
     }
 
+    ctx->options |= flags;
     switch (target->nodetype) {
     case LYS_CONTAINER:
         COMPILE_ARRAY1_GOTO(ctx, aug_p->actions, ((struct lysc_node_container*)target)->actions, target,
-                            options | flags, u, lys_compile_action, 0, ret, error);
-        /* TODO notifications */
+                            u, lys_compile_action, 0, ret, error);
+        COMPILE_ARRAY1_GOTO(ctx, aug_p->notifs, ((struct lysc_node_container*)target)->notifs, target,
+                            u, lys_compile_notif, 0, ret, error);
         break;
     case LYS_LIST:
         COMPILE_ARRAY1_GOTO(ctx, aug_p->actions, ((struct lysc_node_list*)target)->actions, target,
-                            options | flags, u, lys_compile_action, 0, ret, error);
-        /* TODO notifications */
+                            u, lys_compile_action, 0, ret, error);
+        COMPILE_ARRAY1_GOTO(ctx, aug_p->notifs, ((struct lysc_node_list*)target)->notifs, target,
+                            u, lys_compile_notif, 0, ret, error);
         break;
     default:
+        ctx->options = opt_prev;
         if (aug_p->actions) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
                    "Invalid augment (%s) of %s node which is not allowed to contain RPC/action node \"%s\".",
@@ -4179,6 +4267,7 @@
     }
 
 error:
+    ctx->options = opt_prev;
     return ret;
 }
 
@@ -4254,14 +4343,13 @@
  *
  * @param[in] ctx Compile context
  * @param[in] uses_p Parsed uses schema node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in] parent Compiled parent node where the content of the referenced grouping is supposed to be connected. It is
  * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
  * the compile context.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_uses(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, int options, struct lysc_node *parent)
+lys_compile_uses(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, struct lysc_node *parent)
 {
     struct lysp_node *node_p;
     struct lysc_node *node, *child;
@@ -4271,8 +4359,9 @@
          .module = ctx->mod,
          .flags = parent ? parent->flags : 0,
          .child = NULL, .next = NULL,
-         .prev = (struct lysc_node*)&context_node_fake};
-    const struct lysp_grp *grp = NULL;
+         .prev = (struct lysc_node*)&context_node_fake,
+         .actions = NULL, .notifs = NULL};
+    struct lysp_grp *grp = NULL;
     unsigned int u, v, grp_stack_count;
     int found;
     const char *id, *name, *prefix;
@@ -4285,11 +4374,14 @@
     struct ly_set refined = {0};
     struct lysc_when **when, *when_shared;
     struct lysp_augment **augments = NULL;
+    unsigned int actions_index, notifs_index;
+    struct lysc_notif **notifs = NULL;
+    struct lysc_action **actions = NULL;
 
     /* search for the grouping definition */
     found = 0;
     id = uses_p->name;
-    lys_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len);
+    LY_CHECK_RET(lys_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len), LY_EVALID);
     if (prefix) {
         mod = lys_module_find_prefix(ctx->mod_def, prefix, prefix_len);
         if (!mod) {
@@ -4302,7 +4394,7 @@
     }
     if (mod == ctx->mod_def) {
         for (node_p = uses_p->parent; !found && node_p; node_p = node_p->parent) {
-            grp = lysp_node_groupings(node_p);
+            grp = (struct lysp_grp*)lysp_node_groupings(node_p);
             LY_ARRAY_FOR(grp, u) {
                 if (!strcmp(grp[u].name, name)) {
                     grp = &grp[u];
@@ -4353,34 +4445,45 @@
                "Grouping \"%s\" references itself through a uses statement.", grp->name);
         return LY_EVALID;
     }
+    if (!(ctx->options & LYSC_OPT_GROUPING)) {
+        /* remember that the grouping is instantiated to avoid its standalone validation */
+        grp->flags |= LYS_USED_GRP;
+    }
 
     /* switch context's mod_def */
     mod_old = ctx->mod_def;
     ctx->mod_def = mod;
 
     /* check status */
-    LY_CHECK_GOTO(lysc_check_status(ctx, uses_p->flags, mod_old, uses_p->name, grp->flags, mod, grp->name), error);
+    LY_CHECK_GOTO(lysc_check_status(ctx, uses_p->flags, mod_old, uses_p->name, grp->flags, mod, grp->name), cleanup);
 
+    /* compile data nodes */
     LY_LIST_FOR(grp->data, node_p) {
         /* 0x3 in uses_status is a special bits combination to be able to detect status flags from uses */
-        LY_CHECK_GOTO(lys_compile_node(ctx, node_p, options, parent, (uses_p->flags & LYS_STATUS_MASK) | 0x3), error);
-        child = parent ? lysc_node_children(parent, options & LYSC_OPT_RPC_MASK)->prev : ctx->mod->compiled->data->prev;
+        LY_CHECK_GOTO(lys_compile_node(ctx, node_p, parent, (uses_p->flags & LYS_STATUS_MASK) | 0x3), cleanup);
 
         /* some preparation for applying refines */
         if (grp->data == node_p) {
             /* remember the first child */
-            context_node_fake.child = child;
+            if (parent) {
+                child = (struct lysc_node*)lysc_node_children(parent, ctx->options & LYSC_OPT_RPC_MASK);
+            } else if (ctx->mod->compiled->data) {
+                child = ctx->mod->compiled->data;
+            } else {
+                child = NULL;
+            }
+            context_node_fake.child = child ? child->prev : NULL;
         }
     }
     when_shared = NULL;
     LY_LIST_FOR(context_node_fake.child, child) {
         child->parent = (struct lysc_node*)&context_node_fake;
 
-        /* pass uses's when to all the children */
+        /* pass uses's when to all the data children, actions and notifications are ignored */
         if (uses_p->when) {
-            LY_ARRAY_NEW_GOTO(ctx->ctx, child->when, when, ret, error);
+            LY_ARRAY_NEW_GOTO(ctx->ctx, child->when, when, ret, cleanup);
             if (!when_shared) {
-                LY_CHECK_GOTO(lys_compile_when(ctx, uses_p->when, options, when), error);
+                LY_CHECK_GOTO(lys_compile_when(ctx, uses_p->when, when), cleanup);
                 (*when)->context = lysc_xpath_context(parent);
                 when_shared = *when;
             } else {
@@ -4390,14 +4493,43 @@
         }
     }
     if (context_node_fake.child) {
+        /* child is the last data node added by grouping */
         child = context_node_fake.child->prev;
-        context_node_fake.child->prev = parent ? lysc_node_children(parent, options & LYSC_OPT_RPC_MASK)->prev : ctx->mod->compiled->data->prev;
+        /* fix child link of our fake container to point to the first child of the original list */
+        context_node_fake.child->prev = parent ? lysc_node_children(parent, ctx->options & LYSC_OPT_RPC_MASK)->prev : ctx->mod->compiled->data->prev;
     }
 
+    /* compile actions */
+    actions = parent ? lysc_node_actions_p(parent) : &ctx->mod->compiled->rpcs;
+    if (actions) {
+        actions_index = *actions ? LY_ARRAY_SIZE(*actions) : 0;
+        COMPILE_ARRAY1_GOTO(ctx, grp->actions, *actions, parent, u, lys_compile_action, 0, ret, cleanup);
+        if (*actions && (uses_p->augments || uses_p->refines)) {
+            /* but for augment and refine, we need to separate the compiled grouping's actions to avoid modification of others */
+            LY_ARRAY_CREATE_GOTO(ctx->ctx, context_node_fake.actions, LY_ARRAY_SIZE(*actions) - actions_index, ret, cleanup);
+            LY_ARRAY_SIZE(context_node_fake.actions) = LY_ARRAY_SIZE(*actions) - actions_index;
+            memcpy(context_node_fake.actions, &(*actions)[actions_index], LY_ARRAY_SIZE(context_node_fake.actions) * sizeof **actions);
+        }
+    }
+
+    /* compile notifications */
+    notifs = parent ? lysc_node_notifs_p(parent) : &ctx->mod->compiled->notifs;
+    if (notifs) {
+        notifs_index = *notifs ? LY_ARRAY_SIZE(*notifs) : 0;
+        COMPILE_ARRAY1_GOTO(ctx, grp->notifs, *notifs, parent, u, lys_compile_notif, 0, ret, cleanup);
+        if (*notifs && (uses_p->augments || uses_p->refines)) {
+            /* but for augment and refine, we need to separate the compiled grouping's notification to avoid modification of others */
+            LY_ARRAY_CREATE_GOTO(ctx->ctx, context_node_fake.notifs, LY_ARRAY_SIZE(*notifs) - notifs_index, ret, cleanup);
+            LY_ARRAY_SIZE(context_node_fake.notifs) = LY_ARRAY_SIZE(*notifs) - notifs_index;
+            memcpy(context_node_fake.notifs, &(*notifs)[notifs_index], LY_ARRAY_SIZE(context_node_fake.notifs) * sizeof **notifs);
+        }
+    }
+
+
     /* sort and apply augments */
-    LY_CHECK_GOTO(lys_compile_augment_sort(ctx, uses_p->augments, NULL, &augments), error);
+    LY_CHECK_GOTO(lys_compile_augment_sort(ctx, uses_p->augments, NULL, &augments), cleanup);
     LY_ARRAY_FOR(augments, u) {
-        LY_CHECK_GOTO(lys_compile_augment(ctx, augments[u], options, (struct lysc_node*)&context_node_fake), error);
+        LY_CHECK_GOTO(lys_compile_augment(ctx, augments[u], (struct lysc_node*)&context_node_fake), cleanup);
     }
 
     /* reload previous context's mod_def */
@@ -4407,7 +4539,7 @@
     LY_ARRAY_FOR(uses_p->refines, struct lysp_refine, rfn) {
         LY_CHECK_GOTO(lys_resolve_schema_nodeid(ctx, rfn->nodeid, 0, (struct lysc_node*)&context_node_fake, ctx->mod,
                                                 0, 0, (const struct lysc_node**)&node, &flags),
-                      error);
+                      cleanup);
         ly_set_add(&refined, node, LY_SET_OPT_USEASLIST);
 
         /* default value */
@@ -4416,13 +4548,13 @@
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                        "Invalid refine of default in \"%s\" - %s cannot hold %d default values.",
                        rfn->nodeid, lys_nodetype2str(node->nodetype), LY_ARRAY_SIZE(rfn->dflts));
-                goto error;
+                goto cleanup;
             }
             if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CHOICE))) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                        "Invalid refine of default in \"%s\" - %s cannot hold default value(s).",
                        rfn->nodeid, lys_nodetype2str(node->nodetype));
-                goto error;
+                goto cleanup;
             }
             if (node->nodetype == LYS_LEAF) {
                 FREE_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->dflt);
@@ -4433,14 +4565,14 @@
                 if (ctx->mod->version < 2) {
                     LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                            "Invalid refine of default in leaf-list - the default statement is allowed only in YANG 1.1 modules.");
-                    goto error;
+                    goto cleanup;
                 }
                 LY_ARRAY_FOR(((struct lysc_node_leaflist*)node)->dflts, u) {
                     lydict_remove(ctx->ctx, ((struct lysc_node_leaflist*)node)->dflts[u]);
                 }
                 LY_ARRAY_FREE(((struct lysc_node_leaflist*)node)->dflts);
                 ((struct lysc_node_leaflist*)node)->dflts = NULL;
-                LY_ARRAY_CREATE_GOTO(ctx->ctx, ((struct lysc_node_leaflist*)node)->dflts, LY_ARRAY_SIZE(rfn->dflts), ret, error);
+                LY_ARRAY_CREATE_GOTO(ctx->ctx, ((struct lysc_node_leaflist*)node)->dflts, LY_ARRAY_SIZE(rfn->dflts), ret, cleanup);
                 LY_ARRAY_FOR(rfn->dflts, u) {
                     LY_ARRAY_INCREMENT(((struct lysc_node_leaflist*)node)->dflts);
                     DUP_STRING(ctx->ctx, rfn->dflts[u], ((struct lysc_node_leaflist*)node)->dflts[u]);
@@ -4451,7 +4583,7 @@
                     /* unset LYS_SET_DFLT from the current default case */
                     ((struct lysc_node_choice*)node)->dflt->flags &= ~LYS_SET_DFLT;
                 }
-                LY_CHECK_GOTO(lys_compile_node_choice_dflt(ctx, rfn->dflts[0], (struct lysc_node_choice*)node), error);
+                LY_CHECK_GOTO(lys_compile_node_choice_dflt(ctx, rfn->dflts[0], (struct lysc_node_choice*)node), cleanup);
             }
         }
 
@@ -4470,7 +4602,7 @@
         /* config */
         if (rfn->flags & LYS_CONFIG_MASK) {
             if (!flags) {
-                LY_CHECK_GOTO(lys_compile_change_config(ctx, node, rfn->flags, rfn->nodeid, 0, 1), error);
+                LY_CHECK_GOTO(lys_compile_change_config(ctx, node, rfn->flags, rfn->nodeid, 0, 1), cleanup);
             } else {
                 LOGWRN(ctx->ctx, "Refining config inside %s has no effect (%s).",
                        flags & LYSC_OPT_NOTIFICATION ? "Notification" : "RPC/action", ctx->path);
@@ -4479,7 +4611,7 @@
 
         /* mandatory */
         if (rfn->flags & LYS_MAND_MASK) {
-            LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, node, rfn->flags, rfn->nodeid, 1), error);
+            LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, node, rfn->flags, rfn->nodeid, 1), cleanup);
         }
 
         /* presence */
@@ -4488,7 +4620,7 @@
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                        "Invalid refine of presence statement in \"%s\" - %s cannot hold the presence statement.",
                        rfn->nodeid, lys_nodetype2str(node->nodetype));
-                goto error;
+                goto cleanup;
             }
             node->flags |= LYS_PRESENCE;
         }
@@ -4497,26 +4629,26 @@
         if (rfn->musts) {
             switch (node->nodetype) {
             case LYS_LEAF:
-                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_leaf*)node)->musts, options, u, lys_compile_must, ret, error);
+                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_leaf*)node)->musts, u, lys_compile_must, ret, cleanup);
                 break;
             case LYS_LEAFLIST:
-                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_leaflist*)node)->musts, options, u, lys_compile_must, ret, error);
+                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_leaflist*)node)->musts, u, lys_compile_must, ret, cleanup);
                 break;
             case LYS_LIST:
-                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_list*)node)->musts, options, u, lys_compile_must, ret, error);
+                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_list*)node)->musts, u, lys_compile_must, ret, cleanup);
                 break;
             case LYS_CONTAINER:
-                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_container*)node)->musts, options, u, lys_compile_must, ret, error);
+                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_container*)node)->musts, u, lys_compile_must, ret, cleanup);
                 break;
             case LYS_ANYXML:
             case LYS_ANYDATA:
-                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_anydata*)node)->musts, options, u, lys_compile_must, ret, error);
+                COMPILE_ARRAY_GOTO(ctx, rfn->musts, ((struct lysc_node_anydata*)node)->musts, u, lys_compile_must, ret, cleanup);
                 break;
             default:
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                        "Invalid refine of must statement in \"%s\" - %s cannot hold any must statement.",
                        rfn->nodeid, lys_nodetype2str(node->nodetype));
-                goto error;
+                goto cleanup;
             }
         }
 
@@ -4557,23 +4689,16 @@
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                        "Invalid refine of %s statement in \"%s\" - %s cannot hold this statement.",
                        (rfn->flags & LYS_SET_MAX) ? "max-elements" : "min-elements", rfn->nodeid, lys_nodetype2str(node->nodetype));
-                goto error;
+                goto cleanup;
             }
         }
 
         /* if-feature */
         if (rfn->iffeatures) {
             /* any node in compiled tree can get additional if-feature, so do not check nodetype */
-            COMPILE_ARRAY_GOTO(ctx, rfn->iffeatures, node->iffeatures, options, u, lys_compile_iffeature, ret, error);
+            COMPILE_ARRAY_GOTO(ctx, rfn->iffeatures, node->iffeatures, u, lys_compile_iffeature, ret, cleanup);
         }
     }
-    /* fix connection of the children nodes from fake context node back into the parent */
-    if (context_node_fake.child) {
-        context_node_fake.child->prev = child;
-    }
-    LY_LIST_FOR(context_node_fake.child, child) {
-        child->parent = parent;
-    }
 
     /* do some additional checks of the changed nodes when all the refines are applied */
     for (u = 0; u < refined.count; ++u) {
@@ -4586,7 +4711,7 @@
                 ((node->nodetype & LYS_LEAF) && (node->flags & LYS_SET_DFLT)))) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                    "Invalid refine of default in \"%s\" - the node is mandatory.", rfn->nodeid);
-            goto error;
+            goto cleanup;
         }
 
         if (rfn->flags & (LYS_SET_MAX | LYS_SET_MIN)) {
@@ -4601,13 +4726,34 @@
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                        "Invalid refine of %s statement in \"%s\" - \"min-elements\" is bigger than \"max-elements\".",
                        (rfn->flags & LYS_SET_MAX) ? "max-elements" : "min-elements", rfn->nodeid);
-                goto error;
+                goto cleanup;
             }
         }
     }
 
     ret = LY_SUCCESS;
-error:
+
+cleanup:
+    /* fix connection of the children nodes from fake context node back into the parent */
+    if (context_node_fake.child) {
+        context_node_fake.child->prev = child;
+    }
+    LY_LIST_FOR(context_node_fake.child, child) {
+        child->parent = parent;
+    }
+
+    if (uses_p->augments || uses_p->refines) {
+        /* return back actions and notifications in case they were separated for augment/refine processing */
+        if (context_node_fake.actions) {
+            memcpy(&(*actions)[actions_index], context_node_fake.actions, LY_ARRAY_SIZE(context_node_fake.actions) * sizeof **actions);
+            LY_ARRAY_FREE(context_node_fake.actions);
+        }
+        if (context_node_fake.notifs) {
+            memcpy(&(*notifs)[notifs_index], context_node_fake.notifs, LY_ARRAY_SIZE(context_node_fake.notifs) * sizeof **notifs);
+            LY_ARRAY_FREE(context_node_fake.notifs);
+        }
+    }
+
     /* reload previous context's mod_def */
     ctx->mod_def = mod_old;
     /* remove the grouping from the stack for circular groupings dependency check */
@@ -4619,12 +4765,50 @@
     return ret;
 }
 
+/**
+ * @brief Validate groupings that were defined but not directly used in the schema itself.
+ *
+ * The grouping does not need to be compiled (and it is compiled here, but the result is forgotten immediately),
+ * but to have the complete result of the schema validity, even such groupings are supposed to be checked.
+ */
+static LY_ERR
+lys_compile_grouping(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysp_grp *grp)
+{
+    LY_ERR ret;
+    struct lysp_node_uses fake_uses = {
+        .parent = node_p,
+        .nodetype = LYS_USES,
+        .flags = 0, .next = NULL,
+        .name = grp->name,
+        .dsc = NULL, .ref = NULL, .when = NULL, .iffeatures = NULL, .exts = NULL,
+        .refines = NULL, .augments = NULL
+    };
+    struct lysc_node_container fake_container = {
+        .nodetype = LYS_CONTAINER,
+        .flags = node_p ? (node_p->flags & LYS_FLAGS_COMPILED_MASK) : 0,
+        .module = ctx->mod,
+        .sp = NULL, .parent = NULL, .next = NULL,
+        .prev = (struct lysc_node*)&fake_container,
+        .name = "fake",
+        .dsc = NULL, .ref = NULL, .exts = NULL, .iffeatures = NULL, .when = NULL,
+        .child = NULL, .musts = NULL, .actions = NULL, .notifs = NULL
+    };
+
+    if (grp->parent) {
+        LOGWRN(ctx->ctx, "Locally scoped grouping \"%s\" not used.", grp->name);
+    }
+    ret = lys_compile_uses(ctx, &fake_uses, (struct lysc_node*)&fake_container);
+
+    /* cleanup */
+    lysc_node_container_free(ctx->ctx, &fake_container);
+
+    return ret;
+}
 
 /**
  * @brief Compile parsed schema node information.
  * @param[in] ctx Compile context
  * @param[in] node_p Parsed schema node.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
  * @param[in] parent Compiled parent node where the current node is supposed to be connected. It is
  * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
  * the compile context.
@@ -4633,14 +4817,14 @@
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent, uint16_t uses_status)
+lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysc_node *parent, uint16_t uses_status)
 {
     LY_ERR ret = LY_EVALID;
     struct lysc_node *node;
     struct lysc_node_case *cs;
     struct lysc_when **when;
     unsigned int u;
-    LY_ERR (*node_compile_spec)(struct lysc_ctx*, struct lysp_node*, int, struct lysc_node*);
+    LY_ERR (*node_compile_spec)(struct lysc_ctx*, struct lysp_node*, struct lysc_node*);
 
     switch (node_p->nodetype) {
     case LYS_CONTAINER:
@@ -4669,7 +4853,7 @@
         node_compile_spec = lys_compile_node_any;
         break;
     case LYS_USES:
-        return lys_compile_uses(ctx, (struct lysp_node_uses*)node_p, options, parent);
+        return lys_compile_uses(ctx, (struct lysp_node_uses*)node_p, parent);
     default:
         LOGINT(ctx->ctx);
         return LY_EINT;
@@ -4681,13 +4865,13 @@
     node->flags = node_p->flags & LYS_FLAGS_COMPILED_MASK;
 
     /* config */
-    if (options & (LYSC_OPT_RPC_INPUT | LYSC_OPT_RPC_OUTPUT)) {
+    if (ctx->options & (LYSC_OPT_RPC_INPUT | LYSC_OPT_RPC_OUTPUT)) {
         /* ignore config statements inside RPC/action data */
-        node->flags &= LYS_CONFIG_MASK;
-        node->flags |= (options & LYSC_OPT_RPC_INPUT) ? LYS_CONFIG_W : LYS_CONFIG_R;
-    } else if (options & LYSC_OPT_NOTIFICATION) {
+        node->flags &= ~LYS_CONFIG_MASK;
+        node->flags |= (ctx->options & LYSC_OPT_RPC_INPUT) ? LYS_CONFIG_W : LYS_CONFIG_R;
+    } else if (ctx->options & LYSC_OPT_NOTIFICATION) {
         /* ignore config statements inside Notification data */
-        node->flags &= LYS_CONFIG_MASK;
+        node->flags &= ~LYS_CONFIG_MASK;
         node->flags |= LYS_CONFIG_R;
     } else if (!(node->flags & LYS_CONFIG_MASK)) {
         /* config not explicitely set, inherit it from parent */
@@ -4710,8 +4894,9 @@
     /* *list ordering */
     if (node->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
         if ((node->flags & LYS_CONFIG_R) && (node->flags & LYS_ORDBY_MASK)) {
-            LOGWRN(ctx->ctx, "The ordered-by statement is ignored in lists representing state data, "
-                   "RPC/action output parameters or notification content (%s).", ctx->path);
+            LOGWRN(ctx->ctx, "The ordered-by statement is ignored in lists representing %s (%s).",
+                   (ctx->options & LYSC_OPT_RPC_OUTPUT) ? "RPC/action output parameters" :
+                           (ctx->options & LYSC_OPT_NOTIFICATION) ? "notification content" : "state data", ctx->path);
             node->flags &= ~LYS_ORDBY_MASK;
             node->flags |= LYS_ORDBY_SYSTEM;
         } else if (!(node->flags & LYS_ORDBY_MASK)) {
@@ -4728,7 +4913,7 @@
         LY_CHECK_GOTO(lys_compile_status(ctx, &node->flags, uses_status ? uses_status : (parent ? parent->flags : 0)), error);
     }
 
-    if (!(options & LYSC_OPT_FREE_SP)) {
+    if (!(ctx->options & LYSC_OPT_FREE_SP)) {
         node->sp = node_p;
     }
     DUP_STRING(ctx->ctx, node_p->name, node->name);
@@ -4736,15 +4921,15 @@
     DUP_STRING(ctx->ctx, node_p->ref, node->ref);
     if (node_p->when) {
         LY_ARRAY_NEW_GOTO(ctx->ctx, node->when, when, ret, error);
-        ret = lys_compile_when(ctx, node_p->when, options, when);
+        ret = lys_compile_when(ctx, node_p->when, when);
         LY_CHECK_GOTO(ret, error);
         (*when)->context = lysc_xpath_context(node);
     }
-    COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, node->iffeatures, options, u, lys_compile_iffeature, ret, error);
-    COMPILE_ARRAY_GOTO(ctx, node_p->exts, node->exts, options, u, lys_compile_ext, ret, error);
+    COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, node->iffeatures, u, lys_compile_iffeature, ret, error);
+    COMPILE_ARRAY_GOTO(ctx, node_p->exts, node->exts, u, lys_compile_ext, ret, error);
 
     /* nodetype-specific part */
-    LY_CHECK_GOTO(node_compile_spec(ctx, node_p, options, node), error);
+    LY_CHECK_GOTO(node_compile_spec(ctx, node_p, node), error);
 
     /* inherit LYS_MAND_TRUE in parent containers */
     if (node->flags & LYS_MAND_TRUE) {
@@ -4754,7 +4939,7 @@
     /* insert into parent's children */
     if (parent) {
         if (parent->nodetype == LYS_CHOICE) {
-            cs = lys_compile_node_case(ctx, node_p->parent, options, (struct lysc_node_choice*)parent, node);
+            cs = lys_compile_node_case(ctx, node_p->parent, (struct lysc_node_choice*)parent, node);
             LY_CHECK_ERR_GOTO(!cs, ret = LY_EVALID, error);
             if (uses_status) {
 
@@ -4767,7 +4952,7 @@
         } else { /* other than choice */
             node->parent = parent;
         }
-        LY_CHECK_RET(lys_compile_node_connect(ctx, parent->nodetype == LYS_CASE ? parent->parent : parent, node, options), LY_EVALID);
+        LY_CHECK_RET(lys_compile_node_connect(ctx, parent->nodetype == LYS_CASE ? parent->parent : parent, node), LY_EVALID);
     } else {
         /* top-level element */
         if (!ctx->mod->compiled->data) {
@@ -4888,14 +5073,15 @@
 }
 
 LY_ERR
-lys_compile_deviations(struct lysc_ctx *ctx, struct lysp_module *mod_p, int options)
+lys_compile_deviations(struct lysc_ctx *ctx, struct lysp_module *mod_p)
 {
     LY_ERR ret = LY_EVALID;
     struct ly_set devs_p = {0};
     struct ly_set targets = {0};
     struct lysc_node *target; /* target target of the deviation */
     struct lysc_node_list *list;
-    struct lysc_action *actions;
+    struct lysc_action *rpcs;
+    struct lysc_notif *notifs;
     struct lysp_deviation *dev;
     struct lysp_deviate *d, **dp_new;
     struct lysp_deviate_add *d_add;
@@ -5065,35 +5251,42 @@
                 LOGWRN(ctx->ctx, "Useless multiple (%u) deviates on node \"%s\" since the node is not-supported.",
                        LY_ARRAY_SIZE(devs[u]->deviates), devs[u]->nodeid);
             }
+
+#define REMOVE_NONDATA(ARRAY, TYPE, GETFUNC, FREEFUNC) \
+    if (devs[u]->target->parent) { \
+        ARRAY = (TYPE*)GETFUNC(devs[u]->target->parent); \
+    } else { \
+        ARRAY = devs[u]->target->module->compiled->ARRAY; \
+    } \
+    LY_ARRAY_FOR(ARRAY, x) { \
+        if (&ARRAY[x] == (TYPE*)devs[u]->target) { break; } \
+    } \
+    if (x < LY_ARRAY_SIZE(ARRAY)) { \
+        FREEFUNC(ctx->ctx, &ARRAY[x]); \
+        memmove(&ARRAY[x], &ARRAY[x + 1], (LY_ARRAY_SIZE(ARRAY) - (x + 1)) * sizeof *ARRAY); \
+        LY_ARRAY_DECREMENT(ARRAY); \
+    }
+
             if (devs[u]->target->nodetype == LYS_ACTION) {
                 if (devs[u]->flags & LYSC_OPT_RPC_INPUT) {
                     /* remove RPC's/action's input */
                     lysc_action_inout_free(ctx->ctx, &((struct lysc_action*)devs[u]->target)->input);
                     memset(&((struct lysc_action*)devs[u]->target)->input, 0, sizeof ((struct lysc_action*)devs[u]->target)->input);
+                    FREE_ARRAY(ctx->ctx, ((struct lysc_action*)devs[u]->target)->input_exts, lysc_ext_instance_free);
+                    ((struct lysc_action*)devs[u]->target)->input_exts = NULL;
                 } else if (devs[u]->flags & LYSC_OPT_RPC_OUTPUT) {
                     /* remove RPC's/action's output */
                     lysc_action_inout_free(ctx->ctx, &((struct lysc_action*)devs[u]->target)->output);
                     memset(&((struct lysc_action*)devs[u]->target)->output, 0, sizeof ((struct lysc_action*)devs[u]->target)->output);
+                    FREE_ARRAY(ctx->ctx, ((struct lysc_action*)devs[u]->target)->output_exts, lysc_ext_instance_free);
+                    ((struct lysc_action*)devs[u]->target)->output_exts = NULL;
                 } else {
                     /* remove RPC/action */
-                    if (devs[u]->target->parent) {
-                        actions = (struct lysc_action*)lysc_node_actions(devs[u]->target->parent);
-                    } else {
-                        actions = devs[u]->target->module->compiled->rpcs;
-                    }
-                    LY_ARRAY_FOR(actions, x) {
-                        if (&actions[x] == (struct lysc_action*)devs[u]->target) {
-                            break;
-                        }
-                    }
-                    if (x < LY_ARRAY_SIZE(actions)) {
-                        lysc_action_free(ctx->ctx, &actions[x]);
-                        memmove(&actions[x], &actions[x + 1], (LY_ARRAY_SIZE(actions) - (x + 1)) * sizeof *actions);
-                        LY_ARRAY_DECREMENT(actions);
-                    }
+                    REMOVE_NONDATA(rpcs, struct lysc_action, lysc_node_actions, lysc_action_free);
                 }
             } else if (devs[u]->target->nodetype == LYS_NOTIF) {
-                /* TODO Notification */
+                /* remove Notification */
+                REMOVE_NONDATA(notifs, struct lysc_notif, lysc_node_notifs, lysc_notif_free);
             } else {
                 /* remove the target node */
                 lysc_disconnect(devs[u]->target);
@@ -5125,25 +5318,26 @@
                     case LYS_CONTAINER:
                     case LYS_LIST:
                         COMPILE_ARRAY_GOTO(ctx, d_add->musts, ((struct lysc_node_container*)devs[u]->target)->musts,
-                                           options, x, lys_compile_must, ret, cleanup);
+                                           x, lys_compile_must, ret, cleanup);
                         break;
                     case LYS_LEAF:
                     case LYS_LEAFLIST:
                     case LYS_ANYDATA:
                         COMPILE_ARRAY_GOTO(ctx, d_add->musts, ((struct lysc_node_leaf*)devs[u]->target)->musts,
-                                           options, x, lys_compile_must, ret, cleanup);
+                                           x, lys_compile_must, ret, cleanup);
                         break;
                     case LYS_NOTIF:
-                        /* TODO */
+                        COMPILE_ARRAY_GOTO(ctx, d_add->musts, ((struct lysc_notif*)devs[u]->target)->musts,
+                                           x, lys_compile_must, ret, cleanup);
                         break;
                     case LYS_ACTION:
                         if (devs[u]->flags & LYSC_OPT_RPC_INPUT) {
                             COMPILE_ARRAY_GOTO(ctx, d_add->musts, ((struct lysc_action*)devs[u]->target)->input.musts,
-                                               options, x, lys_compile_must, ret, cleanup);
+                                               x, lys_compile_must, ret, cleanup);
                             break;
                         } else  if (devs[u]->flags & LYSC_OPT_RPC_OUTPUT) {
                             COMPILE_ARRAY_GOTO(ctx, d_add->musts, ((struct lysc_action*)devs[u]->target)->output.musts,
-                                               options, x, lys_compile_must, ret, cleanup);
+                                               x, lys_compile_must, ret, cleanup);
                             break;
                         }
                         /* fall through */
@@ -5303,7 +5497,7 @@
                         DEV_DEL_ARRAY(struct lysc_node_leaf*, musts, musts, .arg, .cond->expr, &, lysc_must_free, "must");
                         break;
                     case LYS_NOTIF:
-                        /* TODO */
+                        DEV_DEL_ARRAY(struct lysc_notif*, musts, musts, .arg, .cond->expr, &, lysc_must_free, "must");
                         break;
                     case LYS_ACTION:
                         if (devs[u]->flags & LYSC_OPT_RPC_INPUT) {
@@ -5433,7 +5627,7 @@
                     DEV_CHECK_NODETYPE(LYS_LEAF | LYS_LEAFLIST, "replace", "type");
                     /* type is mandatory, so checking for its presence is not necessary */
                     lysc_type_free(ctx->ctx, ((struct lysc_node_leaf*)devs[u]->target)->type);
-                    LY_CHECK_GOTO(lys_compile_node_type(ctx, NULL, d_rpl->type, options, (struct lysc_node_leaf*)devs[u]->target), cleanup);
+                    LY_CHECK_GOTO(lys_compile_node_type(ctx, NULL, d_rpl->type, (struct lysc_node_leaf*)devs[u]->target), cleanup);
                 }
 
                 /* [units-stmt] */
@@ -5602,11 +5796,10 @@
  * @brief Compile the given YANG submodule into the main module.
  * @param[in] ctx Compile context
  * @param[in] inc Include structure from the main module defining the submodule.
- * @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_submodule(struct lysc_ctx *ctx, struct lysp_include *inc, int options)
+lys_compile_submodule(struct lysc_ctx *ctx, struct lysp_include *inc)
 {
     unsigned int u;
     LY_ERR ret = LY_SUCCESS;
@@ -5618,17 +5811,17 @@
         /* 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.
          * The features compilation is finished in the main module (lys_compile()). */
-        ret = lys_feature_precompile(ctx->ctx, submod->features,
+        ret = lys_feature_precompile(ctx->ctx, ctx->mod, submod->features,
                                      mainmod->mod->off_features ? &mainmod->mod->off_features : &mainmod->features);
         LY_CHECK_GOTO(ret, error);
     }
 
-    COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->identities, mainmod->identities, options, u, lys_compile_identity, ret, error);
+    COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->identities, mainmod->identities, u, lys_compile_identity, ret, error);
 
     /* TODO data nodes */
 
-    COMPILE_ARRAY1_GOTO(ctx, submod->rpcs, mainmod->rpcs, NULL, options, u, lys_compile_action, 0, ret, error);
-    // TODO COMPILE_ARRAY1_GOTO(&ctx, submod->notifs, mainmod->notifs, NULL, options, u, lys_compile_notif, 0, ret, error);
+    COMPILE_ARRAY1_GOTO(ctx, submod->rpcs, mainmod->rpcs, NULL, u, lys_compile_action, 0, ret, error);
+    COMPILE_ARRAY1_GOTO(ctx, submod->notifs, mainmod->notifs, NULL, u, lys_compile_notif, 0, ret, error);
 
 error:
     return ret;
@@ -5643,6 +5836,7 @@
     struct lysp_module *sp;
     struct lysp_node *node_p;
     struct lysp_augment **augments = NULL;
+    struct lysp_grp *grps;
     struct lys_module *m;
     unsigned int u, v;
     LY_ERR ret = LY_SUCCESS;
@@ -5659,14 +5853,15 @@
     ctx.ctx = mod->ctx;
     ctx.mod = mod;
     ctx.mod_def = mod;
+    ctx.options = options;
 
     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, options, u, lys_compile_import, ret, error);
+    COMPILE_ARRAY_GOTO(&ctx, sp->imports, mod_c->imports, u, lys_compile_import, ret, error);
     LY_ARRAY_FOR(sp->includes, u) {
-        ret = lys_compile_submodule(&ctx, &sp->includes[u], options);
+        ret = lys_compile_submodule(&ctx, &sp->includes[u]);
         LY_CHECK_GOTO(ret != LY_SUCCESS, error);
     }
     if (mod->off_features) {
@@ -5676,50 +5871,50 @@
     } 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 */
-        ret = lys_feature_precompile(ctx.ctx, sp->features, &mod_c->features);
+        ret = lys_feature_precompile(ctx.ctx, ctx.mod, sp->features, &mod_c->features);
         LY_CHECK_GOTO(ret, error);
     }
     /* finish feature compilation, not only for the main module, but also for the submodules.
      * Due to possible forward references, it must be done when all the features (including submodules)
      * are present. */
     LY_ARRAY_FOR(sp->features, u) {
-        ret = lys_feature_precompile_finish(&ctx, &sp->features[u], options, mod_c->features);
+        ret = lys_feature_precompile_finish(&ctx, &sp->features[u], mod_c->features);
         LY_CHECK_GOTO(ret != LY_SUCCESS, error);
     }
     LY_ARRAY_FOR(sp->includes, v) {
         LY_ARRAY_FOR(sp->includes[v].submodule->features, u) {
-            ret = lys_feature_precompile_finish(&ctx, &sp->includes[v].submodule->features[u], options, mod_c->features);
+            ret = lys_feature_precompile_finish(&ctx, &sp->includes[v].submodule->features[u], mod_c->features);
             LY_CHECK_GOTO(ret != LY_SUCCESS, error);
         }
     }
 
-    COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, options, u, lys_compile_identity, ret, error);
+    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));
     }
 
     /* data nodes */
     LY_LIST_FOR(sp->data, node_p) {
-        ret = lys_compile_node(&ctx, node_p, options, NULL, 0);
+        ret = lys_compile_node(&ctx, node_p, NULL, 0);
         LY_CHECK_GOTO(ret, error);
     }
 
-    COMPILE_ARRAY1_GOTO(&ctx, sp->rpcs, mod_c->rpcs, NULL, options, u, lys_compile_action, 0, ret, error);
-    // TODO COMPILE_ARRAY1_GOTO(&ctx, sp->notifs, mod_c->notifs, NULL, options, u, lys_compile_notif, 0, ret, error);
+    COMPILE_ARRAY1_GOTO(&ctx, sp->rpcs, mod_c->rpcs, NULL, u, lys_compile_action, 0, ret, error);
+    COMPILE_ARRAY1_GOTO(&ctx, sp->notifs, mod_c->notifs, NULL, u, lys_compile_notif, 0, ret, error);
 
     /* augments - sort first to cover augments augmenting other augments */
     ret = lys_compile_augment_sort(&ctx, sp->augments, sp->includes, &augments);
     LY_CHECK_GOTO(ret, error);
     LY_ARRAY_FOR(augments, u) {
-        ret = lys_compile_augment(&ctx, augments[u], options, NULL);
+        ret = lys_compile_augment(&ctx, augments[u], NULL);
         LY_CHECK_GOTO(ret, error);
     }
 
     /* deviations */
-    ret = lys_compile_deviations(&ctx, sp, options);
+    ret = lys_compile_deviations(&ctx, sp);
     LY_CHECK_GOTO(ret, error);
 
-    COMPILE_ARRAY_GOTO(&ctx, sp->exts, mod_c->exts, options, u, lys_compile_ext, ret, error);
+    COMPILE_ARRAY_GOTO(&ctx, sp->exts, mod_c->exts, u, lys_compile_ext, ret, error);
 
     /* validate leafref's paths and when/must xpaths */
     /* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
@@ -5766,16 +5961,35 @@
             }
         }
     }
+
+    /* validate non-instantiated groupings from the parsed schema,
+     * without it we would accept even the schemas with invalid grouping specification */
+    ctx.options |= LYSC_OPT_GROUPING;
+    LY_ARRAY_FOR(sp->groupings, u) {
+        if (!(sp->groupings[u].flags & LYS_USED_GRP)) {
+            LY_CHECK_GOTO((ret = lys_compile_grouping(&ctx, node_p, &sp->groupings[u])) != LY_SUCCESS, error);
+        }
+    }
+    LY_LIST_FOR(sp->data, node_p) {
+        grps = (struct lysp_grp*)lysp_node_groupings(node_p);
+        LY_ARRAY_FOR(grps, u) {
+            if (!(grps[u].flags & LYS_USED_GRP)) {
+                LY_CHECK_GOTO((ret = lys_compile_grouping(&ctx, node_p, &grps[u])) != LY_SUCCESS, error);
+            }
+        }
+    }
+
     ly_set_erase(&ctx.unres, NULL);
     ly_set_erase(&ctx.groupings, NULL);
+    ly_set_erase(&ctx.tpdf_chain, NULL);
     LY_ARRAY_FREE(augments);
 
-    if (options & LYSC_OPT_FREE_SP) {
+    if (ctx.options & LYSC_OPT_FREE_SP) {
         lysp_module_free(mod->parsed);
         ((struct lys_module*)mod)->parsed = NULL;
     }
 
-    if (!(options & LYSC_OPT_INTERNAL)) {
+    if (!(ctx.options & LYSC_OPT_INTERNAL)) {
         /* remove flag of the modules implemented by dependency */
         for (u = 0; u < ctx.ctx->list.count; ++u) {
             m = ctx.ctx->list.objs[u];
@@ -5792,6 +6006,7 @@
     lys_feature_precompile_revert(&ctx, mod);
     ly_set_erase(&ctx.unres, NULL);
     ly_set_erase(&ctx.groupings, NULL);
+    ly_set_erase(&ctx.tpdf_chain, NULL);
     LY_ARRAY_FREE(augments);
     lysc_module_free(mod_c, NULL);
     mod->compiled = NULL;
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 062524d..546b4d2 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -1,9 +1,9 @@
 /**
- * @file tree_schema.c
+ * @file tree_schema_free.c
  * @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief Schema tree implementation
+ * @brief Freeing functions for schema tree structures.
  *
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 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.
@@ -14,17 +14,16 @@
 
 #include "common.h"
 
-#include "libyang.h"
-#include "context.h"
+#include <stdlib.h>
+
+#include "dict.h"
+#include "tree.h"
+#include "tree_schema.h"
 #include "tree_schema_internal.h"
 #include "xpath.h"
 
-#define FREE_ARRAY(CTX, ARRAY, FUNC) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FUNC(CTX, &(ARRAY)[c__]);}LY_ARRAY_FREE(ARRAY);}
-#define FREE_MEMBER(CTX, MEMBER, FUNC) if (MEMBER) {FUNC(CTX, MEMBER);free(MEMBER);}
-#define FREE_STRINGS(CTX, ARRAY) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FREE_STRING(CTX, ARRAY[c__]);}LY_ARRAY_FREE(ARRAY);}
-
-static void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp);
-static void lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node);
+void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp);
+void lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node);
 
 static void
 lysp_stmt_free(struct ly_ctx *ctx, struct lysp_stmt *stmt)
@@ -41,7 +40,7 @@
     free(stmt);
 }
 
-static void
+void
 lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext)
 {
     struct lysp_stmt *stmt, *next;
@@ -95,7 +94,7 @@
     FREE_ARRAY(ctx, ext->exts, lysp_ext_instance_free);
 }
 
-static void
+void
 lysp_feature_free(struct ly_ctx *ctx, struct lysp_feature *feat)
 {
     FREE_STRING(ctx, feat->name);
@@ -105,7 +104,7 @@
     FREE_ARRAY(ctx, feat->exts, lysp_ext_instance_free);
 }
 
-static void
+void
 lysp_ident_free(struct ly_ctx *ctx, struct lysp_ident *ident)
 {
     FREE_STRING(ctx, ident->name);
@@ -185,7 +184,7 @@
 
 }
 
-static void
+void
 lysp_action_free(struct ly_ctx *ctx, struct lysp_action *action)
 {
     FREE_STRING(ctx, action->name);
@@ -199,7 +198,7 @@
     FREE_ARRAY(ctx, action->exts, lysp_ext_instance_free);
 }
 
-static void
+void
 lysp_notif_free(struct ly_ctx *ctx, struct lysp_notif *notif)
 {
     struct lysp_node *node, *next;
@@ -217,7 +216,7 @@
     FREE_ARRAY(ctx, notif->exts, lysp_ext_instance_free);
 }
 
-static void
+void
 lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp)
 {
     struct lysp_node *node, *next;
@@ -244,7 +243,7 @@
     FREE_ARRAY(ctx, when->exts, lysp_ext_instance_free);
 }
 
-static void
+void
 lysp_augment_free(struct ly_ctx *ctx, struct lysp_augment *augment)
 {
     struct lysp_node *node, *next;
@@ -262,7 +261,7 @@
     FREE_ARRAY(ctx, augment->exts, lysp_ext_instance_free);
 }
 
-static void
+void
 lysp_deviate_free(struct ly_ctx *ctx, struct lysp_deviate *d)
 {
     struct lysp_deviate_add *add = (struct lysp_deviate_add*)d;
@@ -291,7 +290,7 @@
     }
 }
 
-static void
+void
 lysp_deviation_free(struct ly_ctx *ctx, struct lysp_deviation *dev)
 {
     struct lysp_deviate *next, *iter;
@@ -319,7 +318,7 @@
     FREE_ARRAY(ctx, ref->exts, lysp_ext_instance_free);
 }
 
-static void
+void
 lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node)
 {
     struct lysp_node *child, *next;
@@ -521,7 +520,7 @@
     FREE_ARRAY(ctx, ident->exts, lysc_ext_instance_free);
 }
 
-static void
+void
 lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat)
 {
     FREE_STRING(ctx, feat->name);
@@ -549,8 +548,8 @@
     if (--(*pattern)->refcount) {
         return;
     }
-    pcre_free((*pattern)->expr);
-    pcre_free_study((*pattern)->expr_extra);
+    pcre2_code_free((*pattern)->code);
+    FREE_STRING(ctx, (*pattern)->expr);
     FREE_STRING(ctx, (*pattern)->eapptag);
     FREE_STRING(ctx, (*pattern)->emsg);
     FREE_STRING(ctx, (*pattern)->dsc);
@@ -560,7 +559,7 @@
 }
 
 static void
-lysc_enum_item_free(struct ly_ctx *ctx, struct lysc_type_enum_item *item)
+lysc_enum_item_free(struct ly_ctx *ctx, struct lysc_type_bitenum_item *item)
 {
     FREE_STRING(ctx, item->name);
     FREE_STRING(ctx, item->dsc);
@@ -585,7 +584,7 @@
         FREE_MEMBER(ctx, ((struct lysc_type_bin*)type)->length, lysc_range_free);
         break;
     case LY_TYPE_BITS:
-        FREE_ARRAY(ctx, (struct lysc_type_enum_item*)((struct lysc_type_bits*)type)->bits, lysc_enum_item_free);
+        FREE_ARRAY(ctx, (struct lysc_type_bitenum_item*)((struct lysc_type_bits*)type)->bits, lysc_enum_item_free);
         break;
     case LY_TYPE_DEC64:
         FREE_MEMBER(ctx, ((struct lysc_type_dec*)type)->range, lysc_range_free);
@@ -625,7 +624,6 @@
     }
     FREE_ARRAY(ctx, type->exts, lysc_ext_instance_free);
     FREE_STRING(ctx, type->dflt);
-
     free(type);
 }
 
@@ -634,7 +632,6 @@
 {
     struct lysc_node *child, *child_next;
 
-    FREE_ARRAY(ctx, inout->exts, lysc_ext_instance_free);
     FREE_ARRAY(ctx, inout->musts, lysc_must_free);
     LY_LIST_FOR_SAFE(inout->data, child_next, child) {
         lysc_node_free(ctx, child);
@@ -649,11 +646,29 @@
     FREE_STRING(ctx, action->ref);
     FREE_ARRAY(ctx, action->iffeatures, lysc_iffeature_free);
     FREE_ARRAY(ctx, action->exts, lysc_ext_instance_free);
+    FREE_ARRAY(ctx, action->input_exts, lysc_ext_instance_free);
     lysc_action_inout_free(ctx, &action->input);
+    FREE_ARRAY(ctx, action->output_exts, lysc_ext_instance_free);
     lysc_action_inout_free(ctx, &action->output);
 }
 
-static void
+void
+lysc_notif_free(struct ly_ctx *ctx, struct lysc_notif *notif)
+{
+    struct lysc_node *child, *child_next;
+
+    FREE_STRING(ctx, notif->name);
+    FREE_STRING(ctx, notif->dsc);
+    FREE_STRING(ctx, notif->ref);
+    FREE_ARRAY(ctx, notif->iffeatures, lysc_iffeature_free);
+    FREE_ARRAY(ctx, notif->exts, lysc_ext_instance_free);
+    FREE_ARRAY(ctx, notif->musts, lysc_must_free);
+    LY_LIST_FOR_SAFE(notif->data, child_next, child) {
+        lysc_node_free(ctx, child);
+    }
+}
+
+void
 lysc_node_container_free(struct ly_ctx *ctx, struct lysc_node_container *node)
 {
     struct lysc_node *child, *child_next;
@@ -663,8 +678,7 @@
     }
     FREE_ARRAY(ctx, node->musts, lysc_must_free);
     FREE_ARRAY(ctx, node->actions, lysc_action_free);
-
-    /* TODO notifs */
+    FREE_ARRAY(ctx, node->notifs, lysc_notif_free);
 }
 
 static void
@@ -712,8 +726,7 @@
     LY_ARRAY_FREE(node->uniques);
 
     FREE_ARRAY(ctx, node->actions, lysc_action_free);
-
-    /* TODO notifs */
+    FREE_ARRAY(ctx, node->notifs, lysc_notif_free);
 }
 
 static void
@@ -796,7 +809,7 @@
         lysc_node_free(ctx, node);
     }
     FREE_ARRAY(ctx, module->rpcs, lysc_action_free);
-    /* TODO notifications */
+    FREE_ARRAY(ctx, module->notifs, lysc_notif_free);
 
     FREE_ARRAY(ctx, module->exts, lysc_ext_instance_free);
 
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index d11c243..8b32c93 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -1,7 +1,7 @@
 /**
  * @file tree_schema_helpers.c
  * @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief Parsing and validation helper functions
+ * @brief Parsing and validation helper functions for schema trees
  *
  * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
  *
@@ -13,18 +13,25 @@
  */
 #include "common.h"
 
+#include <assert.h>
 #include <ctype.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <stdint.h>
 #include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include <string.h>
 #include <unistd.h>
 #include <time.h>
 
-#include "libyang.h"
+#include "context.h"
+#include "dict.h"
+#include "extensions.h"
+#include "hash_table.h"
+#include "log.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_schema.h"
 #include "tree_schema_internal.h"
 
 /**
@@ -201,18 +208,18 @@
 }
 
 LY_ERR
-lysp_check_prefix(struct ly_ctx *ctx, uint64_t *line, struct lysp_import *imports, const char *module_prefix, const char **value)
+lysp_check_prefix(struct lys_parser_ctx *ctx, struct lysp_import *imports, const char *module_prefix, const char **value)
 {
     struct lysp_import *i;
 
     if (module_prefix && &module_prefix != value && !strcmp(module_prefix, *value)) {
-        LOGVAL(ctx, LY_VLOG_LINE, line, LYVE_REFERENCE,
+        LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE,
                "Prefix \"%s\" already used as module prefix.", *value);
         return LY_EEXIST;
     }
     LY_ARRAY_FOR(imports, struct lysp_import, i) {
         if (i->prefix && &i->prefix != value && !strcmp(i->prefix, *value)) {
-            LOGVAL(ctx, LY_VLOG_LINE, line, LYVE_REFERENCE, "Prefix \"%s\" already used to import \"%s\" module.",
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Prefix \"%s\" already used to import \"%s\" module.",
                    *value, i->name);
             return LY_EEXIST;
         }
@@ -244,7 +251,7 @@
 }
 
 LY_ERR
-lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt)
+lysp_check_date(struct lys_parser_ctx *ctx, const char *date, int date_len, const char *stmt)
 {
     int i;
     struct tm tm, tm_;
@@ -487,7 +494,7 @@
  * @return LY_EEXIST in case of collision, LY_SUCCESS otherwise.
  */
 static LY_ERR
-lysp_check_typedef(struct ly_parser_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
+lysp_check_typedef(struct lys_parser_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
                    struct hash_table *tpdfs_global, struct hash_table *tpdfs_scoped)
 {
     struct lysp_node *parent;
@@ -523,7 +530,7 @@
             }
         }
         /* search typedefs in parent's nodes */
-        for (parent = node->parent; parent; parent = node->parent) {
+        for (parent = node->parent; parent; parent = parent->parent) {
             if (lysp_type_match(name, parent)) {
                 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
                        "Invalid name \"%s\" of typedef - name collision with another scoped type.", name);
@@ -562,7 +569,7 @@
 }
 
 LY_ERR
-lysp_check_typedefs(struct ly_parser_ctx *ctx, struct lysp_module *mod)
+lysp_check_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod)
 {
     struct hash_table *ids_global;
     struct hash_table *ids_scoped;
@@ -631,7 +638,7 @@
         /* check revision of the parsed model */
         if (!revs || strcmp(info->revision, revs[0].date)) {
             LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", name,
-                   revs[0].date, info->revision);
+                   revs ? revs[0].date : "none", info->revision);
             return LY_EINVAL;
         }
     }
@@ -679,7 +686,7 @@
 }
 
 LY_ERR
-lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct ly_parser_ctx *main_ctx,
+lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct lys_parser_ctx *main_ctx,
                      void **result)
 {
     int fd;
@@ -747,8 +754,18 @@
     if (!*mod) {
         /* try to get the module from the context */
         if (revision) {
+            /* get the specific revision */
             *mod = (struct lys_module*)ly_ctx_get_module(ctx, name, revision);
+        } else if (implement) {
+            /* prefer the implemented module instead of the latest one */
+            *mod = (struct lys_module*)ly_ctx_get_module_implemented(ctx, name);
+            if (!*mod) {
+                /* there is no implemented module in the context, try to get the latest revision module */
+                goto latest_in_the_context;
+            }
         } else {
+            /* get the requested module of the latest revision in the context */
+latest_in_the_context:
             *mod = (struct lys_module*)ly_ctx_get_module_latest(ctx, name);
         }
     }
@@ -836,9 +853,9 @@
 }
 
 LY_ERR
-lysp_load_submodule(struct ly_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc)
+lysp_load_submodule(struct lys_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc)
 {
-    struct lysp_submodule *submod;
+    struct lysp_submodule *submod = NULL;
     const char *submodule_data = NULL;
     LYS_INFORMAT format = LYS_IN_UNKNOWN;
     void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
@@ -939,6 +956,30 @@
 }
 
 const char *
+lys_prefix_find_module(const struct lys_module *mod, const struct lys_module *import)
+{
+    unsigned int u;
+
+    if (import == mod) {
+        return mod->prefix;
+    }
+
+    if (mod->parsed) {
+        LY_ARRAY_FOR(mod->parsed->imports, u) {
+            if (mod->parsed->imports[u].module == import) {
+                return mod->parsed->imports[u].prefix;
+            }
+        }
+    } else {
+        /* we don't have original information about the import's prefix,
+         * so the prefix of the import module itself is returned instead */
+        return import->prefix;
+    }
+
+    return NULL;
+}
+
+const char *
 lys_nodetype2str(uint16_t nodetype)
 {
     switch(nodetype) {
@@ -962,6 +1003,55 @@
         return "RPC/action";
     case LYS_NOTIF:
         return "Notification";
+    case LYS_USES:
+        return "uses";
+    default:
+        return "unknown";
+    }
+}
+
+const char *
+lys_datatype2str(LY_DATA_TYPE basetype)
+{
+    switch(basetype) {
+    case LY_TYPE_BINARY:
+        return "binary";
+    case LY_TYPE_UINT8:
+        return "uint8";
+    case LY_TYPE_UINT16:
+        return "uint16";
+    case LY_TYPE_UINT32:
+        return "uint32";
+    case LY_TYPE_UINT64:
+        return "uint64";
+    case LY_TYPE_STRING:
+        return "string";
+    case LY_TYPE_BITS:
+        return "bits";
+    case LY_TYPE_BOOL:
+        return "boolean";
+    case LY_TYPE_DEC64:
+        return "decimal64";
+    case LY_TYPE_EMPTY:
+        return "empty";
+    case LY_TYPE_ENUM:
+        return "enumeration";
+    case LY_TYPE_IDENT:
+        return "identityref";
+    case LY_TYPE_INST:
+        return "instance-identifier";
+    case LY_TYPE_LEAFREF:
+        return "leafref";
+    case LY_TYPE_UNION:
+        return "union";
+    case LY_TYPE_INT8:
+        return "int8";
+    case LY_TYPE_INT16:
+        return "int16";
+    case LY_TYPE_INT32:
+        return "int32";
+    case LY_TYPE_INT64:
+        return "int64";
     default:
         return "unknown";
     }
@@ -1099,6 +1189,11 @@
 lysp_node_children(const struct lysp_node *node)
 {
     struct lysp_node **children;
+
+    if (!node) {
+        return NULL;
+    }
+
     children = lysp_node_children_p((struct lysp_node*)node);
     if (children) {
         return *children;
@@ -1183,7 +1278,8 @@
             /* LYS_CONFIG_W, but also the default case */
             return &((struct lysc_action*)node)->input.data;
         }
-    /* TODO Notification */
+    case LYS_NOTIF:
+        return &((struct lysc_notif*)node)->data;
     default:
         return NULL;
     }
@@ -1193,6 +1289,11 @@
 lysc_node_children(const struct lysc_node *node, uint16_t flags)
 {
     struct lysc_node **children;
+
+    if (!node) {
+        return NULL;
+    }
+
     children = lysc_node_children_p((struct lysc_node*)node, flags);
     if (children) {
         return *children;
@@ -1407,3 +1508,18 @@
     }
 
 }
+
+unsigned int
+lysp_ext_instance_iter(struct lysp_ext_instance *ext, unsigned int index, LYEXT_SUBSTMT substmt)
+{
+    LY_CHECK_ARG_RET(NULL, ext, LY_EINVAL);
+
+    for (; index < LY_ARRAY_SIZE(ext); index++) {
+        if (ext[index].insubstmt == substmt) {
+            return index;
+        }
+    }
+
+    return LY_ARRAY_SIZE(ext);
+}
+
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 559c7a1..db61beb 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -15,6 +15,11 @@
 #ifndef LY_TREE_SCHEMA_INTERNAL_H_
 #define LY_TREE_SCHEMA_INTERNAL_H_
 
+#include <stdint.h>
+
+#include "set.h"
+#include "tree_schema.h"
+
 #define LOGVAL_YANG(CTX, ...) LOGVAL((CTX)->ctx, LY_VLOG_LINE, &(CTX)->line, __VA_ARGS__)
 
 /* These 2 macros checks YANG's identifier grammar rule */
@@ -46,7 +51,7 @@
 /**
  * @brief internal context for schema parsers
  */
-struct ly_parser_ctx {
+struct lys_parser_ctx {
     struct ly_ctx *ctx;
     uint64_t line;      /**< line number */
     struct ly_set tpdfs_nodes;
@@ -67,7 +72,9 @@
                                      the target module (mod) */
     struct ly_set groupings;    /**< stack for groupings circular check */
     struct ly_set unres;        /**< to validate leafref's target and xpath of when/must */
+    struct ly_set tpdf_chain;
     uint16_t path_len;
+    int options;                /**< various @ref scflags. */
 #define LYSC_CTX_BUFSIZE 4078
     char path[LYSC_CTX_BUFSIZE];
 };
@@ -81,7 +88,7 @@
  * @param[in] value Newly added prefix value (including its location to distinguish collision with itself).
  * @return LY_EEXIST when prefix is already used in the module, LY_SUCCESS otherwise
  */
-LY_ERR lysp_check_prefix(struct ly_ctx *ctx, uint64_t *line, struct lysp_import *imports, const char *module_prefix, const char **value);
+LY_ERR lysp_check_prefix(struct lys_parser_ctx *ctx, struct lysp_import *imports, const char *module_prefix, const char **value);
 
 /**
  * @brief Check date string (4DIGIT "-" 2DIGIT "-" 2DIGIT)
@@ -92,7 +99,7 @@
  * @param[in] stmt Statement name for error message.
  * @return LY_ERR value.
  */
-LY_ERR lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt);
+LY_ERR lysp_check_date(struct lys_parser_ctx *ctx, const char *date, int date_len, const char *stmt);
 
 /**
  * @brief Check names of typedefs in the parsed module to detect collisions.
@@ -101,7 +108,7 @@
  * @param[in] mod Module where the type is being defined.
  * @return LY_ERR value.
  */
-LY_ERR lysp_check_typedefs(struct ly_parser_ctx *ctx, struct lysp_module *mod);
+LY_ERR lysp_check_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod);
 
 /**
  * @brief Just move the newest revision into the first position, does not sort the rest
@@ -129,7 +136,9 @@
  * @param[in] ctx libyang context.
  * @param[in] name Name of the module to load.
  * @param[in] revison Optional revision of the module to load. If NULL, the newest revision is loaded.
- * @param[in] implement Flag if the loaded module is supposed to be marked as implemented.
+ * @param[in] implement Flag if the loaded module is supposed to be marked as implemented. If revision is NULL and implement flag set,
+ * the implemented module in the context is returned despite it might not be of the latest revision, because in this case the module
+ * of the latest revision can not be made implemented.
  * @param[in] require_parsed Flag to require parsed module structure in case the module is already in the context,
  * but only the compiled structure is available.
  * @param[out] mod Parsed module structure.
@@ -146,7 +155,7 @@
  * submodule is stored into this structure.
  * @return LY_ERR value.
  */
-LY_ERR lysp_load_submodule(struct ly_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc);
+LY_ERR lysp_load_submodule(struct lys_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc);
 
 /**
  * @defgroup scflags Schema compile flags
@@ -156,10 +165,14 @@
  */
 #define LYSC_OPT_RPC_INPUT  LYS_CONFIG_W       /**< Internal option when compiling schema tree of RPC/action input */
 #define LYSC_OPT_RPC_OUTPUT LYS_CONFIG_R       /**< Internal option when compiling schema tree of RPC/action output */
-#define LYSC_OPT_RPC_MASK   LYS_CONFIG_MASK
+#define LYSC_OPT_RPC_MASK   LYS_CONFIG_MASK    /**< mask for the internal RPC options */
 #define LYSC_OPT_FREE_SP    0x04               /**< Free the input printable schema */
 #define LYSC_OPT_INTERNAL   0x08               /**< Internal compilation caused by dependency */
 #define LYSC_OPT_NOTIFICATION 0x10             /**< Internal option when compiling schema tree of Notification */
+
+#define LYSC_OPT_GROUPING   0x20               /** Compiling (validation) of a non-instantiated grouping.
+                                                   In this case not all the restrictions are checked since they can be valid only
+                                                   in the real placement of the grouping. TODO - what specifically is not done */
 /** @} scflags */
 
 /**
@@ -210,6 +223,36 @@
 struct lysc_node **lysc_node_children_p(const struct lysc_node *node, uint16_t flags);
 
 /**
+ * @brief Get address of a node's notifs pointer if any.
+ *
+ * Decides the node's type and in case it has a notifs array, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's notifs member if any, NULL otherwise.
+ */
+struct lysc_notif **lysc_node_notifs_p(struct lysc_node *node);
+
+/**
+ * @brief Get address of a node's actions pointer if any.
+ *
+ * Decides the node's type and in case it has a actions array, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's actions member if any, NULL otherwise.
+ */
+struct lysc_action **lysc_node_actions_p(struct lysc_node *node);
+
+/**
+ * @brief Iterate over the specified type of the extension instances
+ *
+ * @param[in] ext ([Sized array](@ref sizedarrays)) of extensions to explore
+ * @param[in] index Index in the \p ext array where to start searching (first call with 0, the consequent calls with
+ *            the returned index increased by 1 (until the iteration is not terminated by returning LY_ARRAY_SIZE(ext).
+ * @param[in] substmt Type of the extension (its belongins to the specific substatement) to iterate, use
+ *            #LYEXT_SUBSTMT_ALL to go through all the extensions in the array
+ * @result index in the ext array, LY_ARRAY_SIZE(ext) value if not present.
+ */
+unsigned int lysp_ext_instance_iter(struct lysp_ext_instance *ext, unsigned int index, LYEXT_SUBSTMT substmt);
+
+/**
  * @brief Get the covering schema module structure for the given parsed module structure.
  * @param[in] ctx libyang context to search.
  * @param[in] mod Parsed schema structure.
@@ -298,6 +341,8 @@
 /**
  * @brief Find the module referenced by prefix in the provided mod.
  *
+ * Reverse function to lys_prefix_find_module().
+ *
  * @param[in] mod Schema module where the prefix was used.
  * @param[in] prefix Prefix used to reference a module.
  * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
@@ -306,6 +351,20 @@
 struct lys_module *lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len);
 
 /**
+ * @brief Find the prefix used to referenced the import module in the provided mod.
+ *
+ * Reverse function to lys_module_find_prefix().
+ *
+ * Note that original prefixes are present only in the parsed schema. In case it is not available
+ * (only compiled schema available), the own prefix of the import module is returned instead.
+ *
+ * @param[in] mod Schema module where the import module was used.
+ * @param[in] import Module referenced in mod.
+ * @return Prefix of the import module.
+ */
+const char *lys_prefix_find_module(const struct lys_module *mod, const struct lys_module *import);
+
+/**
  * @brief Stringify schema nodetype.
  * @param[in] nodetype Nodetype to stringify.
  * @return Constant string with the name of the node's type.
@@ -313,6 +372,13 @@
 const char *lys_nodetype2str(uint16_t nodetype);
 
 /**
+ * @brief Stringify YANG built-in type.
+ * @param[in] basetype Built-in tyep ID to stringify.
+ * @return Constant string with the name of the built-in type.
+ */
+const char *lys_datatype2str(LY_DATA_TYPE basetype);
+
+/**
  * @brief Parse YANG module from a string.
  *
  * The modules are added into the context and the latest_revision flag is updated.
@@ -344,7 +410,7 @@
  * @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_mem_submodule(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, struct ly_parser_ctx *main_ctx,
+struct lysp_submodule *lys_parse_mem_submodule(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
                                                LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *check_data),
                                                void *check_data);
 
@@ -366,7 +432,7 @@
  * @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.
  */
-void *lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+void *lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct lys_parser_ctx *main_ctx,
                     LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data),
                     void *check_data);
 
@@ -406,7 +472,7 @@
  * @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 ly_parser_ctx *main_ctx,
+struct lysp_submodule *lys_parse_fd_submodule(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
                                               LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *check_data),
                                               void *check_data);
 
@@ -445,7 +511,7 @@
  * If it is a module, it is already in the context!
  * @return LY_ERR value, in case of LY_SUCCESS, the \arg result is always provided.
  */
-LY_ERR lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct ly_parser_ctx *main_ctx,
+LY_ERR lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct lys_parser_ctx *main_ctx,
                             void **result);
 
 /**
@@ -461,13 +527,39 @@
  * if-feature structures.
  *
  * @param[in] ctx libyang context.
+ * @param[in] module Module of the features.
  * @param[in] features_p Array if the parsed features definitions to precompile.
  * @param[in,out] features Pointer to the storage of the (pre)compiled features array where the new features are
  * supposed to be added. The storage is supposed to be initiated to NULL when the first parsed features are going
  * to be processed.
  * @return LY_ERR value.
  */
-LY_ERR lys_feature_precompile(struct ly_ctx *ctx, struct lysp_feature *features_p, struct lysc_feature **features);
+LY_ERR lys_feature_precompile(struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features);
+
+/**
+ * @brief Get the @ref ifftokens from the given position in the 2bits array
+ * (libyang format of the if-feature expression).
+ * @param[in] list The 2bits array with the compiled if-feature expression.
+ * @param[in] pos Position (0-based) to specify from which position get the operator.
+ */
+uint8_t lysc_iff_getop(uint8_t *list, int pos);
+
+/**
+ * @brief Macro to free [sized array](@ref sizedarrays) of items using the provided free function. The ARRAY itself is also freed,
+ * but the memory is not sanitized.
+ */
+#define FREE_ARRAY(CTX, ARRAY, FUNC) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FUNC(CTX, &(ARRAY)[c__]);}LY_ARRAY_FREE(ARRAY);}
+
+/**
+ * @brief Macro to free the specified MEMBER of a structure using the provided free function. The memory is not sanitized.
+ */
+#define FREE_MEMBER(CTX, MEMBER, FUNC) if (MEMBER) {FUNC(CTX, MEMBER);free(MEMBER);}
+
+/**
+ * @brief Macro to free [sized array](@ref sizedarrays) of strings stored in the context's dictionary. The ARRAY itself is also freed,
+ * but the memory is not sanitized.
+ */
+#define FREE_STRINGS(CTX, ARRAY) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FREE_STRING(CTX, ARRAY[c__]);}LY_ARRAY_FREE(ARRAY);}
 
 /**
  * @brief Free the parsed submodule structure.
@@ -516,6 +608,14 @@
 void lysc_action_free(struct ly_ctx *ctx, struct lysc_action *action);
 
 /**
+ * @brief Free the items inside the compiled Notification structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] action Compiled Notification structure to be cleaned.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+void lysc_notif_free(struct ly_ctx *ctx, struct lysc_notif *notif);
+
+/**
  * @brief Free the compiled extension instance structure.
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] ext Compiled extension instance structure to be cleaned.
@@ -531,6 +631,17 @@
 void lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node);
 
 /**
+ * @brief Free the compiled container node structure.
+ *
+ * Only the container-specific members are freed, for generic node free function,
+ * use lysc_node_free().
+ *
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] node Compiled container node structure to be freed.
+ */
+void lysc_node_container_free(struct ly_ctx *ctx, struct lysc_node_container *node);
+
+/**
  * @brief Free the compiled schema structure.
  * @param[in,out] module Compiled schema module structure to free.
  * @param[in] private_destructor Function to remove private data from the compiled schema tree.
@@ -551,7 +662,7 @@
  * @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 ly_parser_ctx *ctx, const char *data, struct lysp_submodule **submod);
+LY_ERR yang_parse_submodule(struct lys_parser_ctx *ctx, const char *data, struct lysp_submodule **submod);
 
 /**
  * @brief Parse module from YANG data.
@@ -561,7 +672,17 @@
  * module structure, will be filled in.
  * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
  */
-LY_ERR yang_parse_module(struct ly_parser_ctx *ctx, const char *data, struct lys_module *mod);
+LY_ERR yang_parse_module(struct lys_parser_ctx *ctx, const char *data, struct lys_module *mod);
+
+/**
+ * @brief Parse module from YIN data.
+ * @param[in] ctx Libyang 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 yin_parse_module(struct ly_ctx *ctx, const char *data, struct lys_module *mod);
 
 /**
  * @brief Make the specific module implemented, use the provided value as flag.
diff --git a/src/xml.c b/src/xml.c
index 147170a..dbf9b18 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -18,10 +18,11 @@
 #include <ctype.h>
 #include <stdbool.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
 
-#include "libyang.h"
 #include "xml.h"
+#include "printer_internal.h"
 
 /* Move input p by s characters, if EOF log with lyxml_context c */
 #define move_input(c,p,s) p += s; LY_CHECK_ERR_RET(!p[0], LOGVAL(c->ctx, LY_VLOG_LINE, &c->line, LY_VCODE_EOF), LY_EVALID)
@@ -167,6 +168,99 @@
     return LY_SUCCESS;
 }
 
+/**
+ * @brief Add namespace definition into XML context.
+ *
+ * Namespaces from a single element are supposed to be added sequentially together (not interleaved by a namespace from other
+ * element). This mimic namespace visibility, since the namespace defined in element E is not visible from its parents or
+ * siblings. On the other hand, namespace from a parent element can be redefined in a child element. This is also reflected
+ * by lyxml_ns_get() which returns the most recent namespace definition for the given prefix.
+ *
+ * When leaving processing of a subtree of some element (after it is removed from context->elements), caller is supposed to call
+ * lyxml_ns_rm() to remove all the namespaces defined in such an element from the context.
+ *
+ * @param[in] context XML context to work with.
+ * @param[in] prefix Pointer to the namespace prefix as taken from lyxml_get_attribute(). Can be NULL for default namespace.
+ * @param[in] prefix_len Length of the prefix string (since it is not NULL-terminated when returned from lyxml_get_attribute()).
+ * @param[in] uri Namespace URI (value) to store. Value can be obtained via lyxml_get_string() and caller is not supposed to
+ * work with the pointer when the function succeeds. In case of error the value is freed.
+ * @return LY_ERR values.
+ */
+LY_ERR
+lyxml_ns_add(struct lyxml_context *context, const char *prefix, size_t prefix_len, char *uri)
+{
+    struct lyxml_ns *ns;
+
+    ns = malloc(sizeof *ns);
+    LY_CHECK_ERR_RET(!ns, LOGMEM(context->ctx), LY_EMEM);
+
+    /* we need to connect the depth of the element where the namespace is defined with the
+     * namespace record to be able to maintain (remove) the record when the parser leaves
+     * (to its sibling or back to the parent) the element where the namespace was defined */
+    ns->depth = context->elements.count;
+
+    ns->uri = uri;
+    if (prefix) {
+        ns->prefix = strndup(prefix, prefix_len);
+        LY_CHECK_ERR_RET(!ns->prefix, LOGMEM(context->ctx); free(ns->uri); free(ns), LY_EMEM);
+    } else {
+        ns->prefix = NULL;
+    }
+
+    LY_CHECK_ERR_RET(ly_set_add(&context->ns, ns, LY_SET_OPT_USEASLIST) == -1,
+                     free(ns->prefix); free(ns->uri); free(ns), LY_EMEM);
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Remove all the namespaces defined in the element recently closed (removed from the context->elements).
+ *
+ * @param[in] context XML context to work with.
+ */
+void
+lyxml_ns_rm(struct lyxml_context *context)
+{
+    unsigned int u;
+
+    for (u = context->ns.count - 1; u + 1 > 0; --u) {
+        if (((struct lyxml_ns *)context->ns.objs[u])->depth != context->elements.count + 1) {
+            /* we are done, the namespaces from a single element are supposed to be together */
+            break;
+        }
+        /* remove the ns structure */
+        free(((struct lyxml_ns *)context->ns.objs[u])->prefix);
+        free(((struct lyxml_ns *)context->ns.objs[u])->uri);
+        free(context->ns.objs[u]);
+        --context->ns.count;
+    }
+
+    if (!context->ns.count) {
+        /* cleanup the context's namespaces storage */
+        ly_set_erase(&context->ns, NULL);
+    }
+}
+
+const struct lyxml_ns *
+lyxml_ns_get(struct lyxml_context *context, const char *prefix, size_t prefix_len)
+{
+    unsigned int u;
+    struct lyxml_ns *ns;
+
+    for (u = context->ns.count - 1; u + 1 > 0; --u) {
+        ns = (struct lyxml_ns *)context->ns.objs[u];
+        if (prefix) {
+            if (!strncmp(prefix, ns->prefix, prefix_len) && ns->prefix[prefix_len] == '\0') {
+                return ns;
+            }
+        } else if (!ns->prefix) {
+            /* default namespace */
+            return ns;
+        }
+    }
+
+    return NULL;
+}
+
 LY_ERR
 lyxml_get_string(struct lyxml_context *context, const char **input, char **buffer, size_t *buffer_size, char **output, size_t *length, int *dynamic)
 {
@@ -189,7 +283,7 @@
     uint32_t n;
     size_t u, newlines;
     bool empty_content = false;
-    LY_ERR rc;
+    LY_ERR rc = LY_SUCCESS;
 
     assert(context);
     assert(context->status == LYXML_ELEM_CONTENT || context->status == LYXML_ATTR_CONTENT);
@@ -217,8 +311,29 @@
         LY_CHECK_ERR_RET(!in[offset], LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_EOF), LY_EVALID);
         context->line += newlines;
         if (in[offset] == '<') {
+            const char *name, *prefix;
+            size_t name_len, prefix_len;
+
             (*input) = in + offset;
-            return LY_EINVAL;
+
+            /* get know if it is child element (indentation) or closing element (whitespace-only content) */
+            in = *input;
+            rc = lyxml_get_element(context, &in, &prefix, &prefix_len, &name, &name_len);
+            if (name) {
+                /* the element here is not closing element, so we have the just indentation formatting before the child */
+                free(context->elements.objs[--context->elements.count]);
+                context->status -= 1; /* LYXML_ELEMENT */
+                return LY_EINVAL;
+            } else if (rc) {
+                /* some parsing error, so pass it */
+                (*input) = in;
+                goto error;
+            } else {
+                /* whitespace-only content */
+                len = offset;
+                context->status++;
+                goto success;
+            }
         }
     }
     /* init */
@@ -332,7 +447,29 @@
             /* in case of element content, keep the leading <,
              * for attribute's value move after the terminating quotation mark */
             if (context->status == LYXML_ELEM_CONTENT) {
+                const char *name, *prefix;
+                size_t name_len, prefix_len;
+
                 in += offset;
+
+                /* get know if it is child element (mixed content) or closing element (regular content) */
+                (*input) = in;
+                rc = lyxml_get_element(context, &in, &prefix, &prefix_len, &name, &name_len);
+                if (name) {
+                    /* the element here is not closing element, so we have not allowed mixed content */
+                    struct lyxml_elem *e = (struct lyxml_elem*)context->elements.objs[--context->elements.count];
+                    LOGVAL(ctx, LY_VLOG_LINE, &context->line, LYVE_SYNTAX, "Mixed XML content is not allowed (%.*s).",
+                           offset + (in - (*input)), &(*input)[-offset]);
+                    free(e);
+                    goto error;
+                } else if (rc) {
+                    /* some parsing error */
+                    goto error;
+                } else {
+                    /* closing element, so we have regular content */
+                    context->status++;
+                    goto success;
+                }
             } else {
                 in += offset + 1;
             }
@@ -350,7 +487,12 @@
     LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_EOF);
 error:
     if (!(*buffer)) {
+        /* buffer not provided, buf is local */
         free(buf);
+    } else if (buf) {
+        /* buf is shared with caller via buffer, but buf could be reallocated, so update the provided buffer */
+        (*buffer) = buf;
+        (*buffer_size) = size;
     }
     return LY_EVALID;
 
@@ -395,11 +537,14 @@
             /* remove the closed element record from the tags list */
             free(context->elements.objs[context->elements.count - 1]);
             --context->elements.count;
+
+            /* remove also the namespaces conneted with the element */
+            lyxml_ns_rm(context);
         }
     }
 
     (*input) = in;
-    return LY_SUCCESS;
+    return rc;
 
 #undef BUFSIZE
 #undef BUFSIZE_STEP
@@ -417,7 +562,11 @@
     LY_ERR rc;
     unsigned int c;
     size_t endtag_len;
+    int is_ns = 0;
+    const char *ns_prefix = NULL;
+    size_t ns_prefix_len = 0;
 
+start:
     /* initialize output variables */
     (*prefix) = (*name) = NULL;
     (*prefix_len) = (*name_len) = 0;
@@ -469,6 +618,36 @@
     }
     context->status = LYXML_ATTR_CONTENT;
 
+    is_ns = 0;
+    if (*prefix && *prefix_len == 5 && !strncmp(*prefix, "xmlns", 5)) {
+        is_ns = 1;
+        ns_prefix = *name;
+        ns_prefix_len = *name_len;
+    } else if (*name_len == 5 && !strncmp(*name, "xmlns", 5)) {
+        is_ns = 1;
+    }
+    if (is_ns) {
+        /* instead of attribute, we have namespace specification,
+         * so process it automatically and then move to another attribute (if any) */
+        char *value = NULL;
+        size_t value_len = 0;
+        int dynamic = 0;
+
+        LY_CHECK_RET(lyxml_get_string(context, &in, &value, &value_len, &value, &value_len, &dynamic));
+        if ((rc = lyxml_ns_add(context, ns_prefix, ns_prefix_len, dynamic ? value : strndup(value, value_len)))) {
+            if (dynamic) {
+                free(value);
+                return rc;
+            }
+        }
+        if (context->status == LYXML_ATTRIBUTE) {
+            goto start;
+        } else {
+            (*prefix) = (*name) = NULL;
+            (*prefix_len) = (*name_len) = 0;
+        }
+    }
+
     /* move caller's input */
     (*input) = in;
     return LY_SUCCESS;
@@ -524,6 +703,9 @@
                 /* Document type declaration - not supported */
                 LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_NSUPP, "Document Type Declaration");
                 return LY_EVALID;
+            } else {
+                LOGVAL(ctx, LY_VLOG_LINE, &context->line, LYVE_SYNTAX, "Unknown XML section \"%.20s\".", &in[-2]);
+                return LY_EVALID;
             }
             in = ign_todelim(in, endtag, endtag_len, &newlines);
             LY_CHECK_ERR_RET(!in, LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_NTERM, sectname), LY_EVALID);
@@ -582,16 +764,20 @@
                 /* match opening and closing element tags */
                 LY_CHECK_ERR_RET(
                         !context->elements.count,
-                        LOGVAL(ctx, LY_VLOG_LINE, &context->line, LYVE_SYNTAX, "Opening and closing elements tag missmatch (\"%.*s\").", name_len, *name),
+                        LOGVAL(ctx, LY_VLOG_LINE, &context->line, LYVE_SYNTAX, "Opening and closing elements tag missmatch (\"%.*s\").", *name_len, *name),
                         LY_EVALID);
                 e = (struct lyxml_elem*)context->elements.objs[context->elements.count - 1];
                 LY_CHECK_ERR_RET(e->prefix_len != *prefix_len || e->name_len != *name_len
                                  || (*prefix_len && strncmp(*prefix, e->prefix, e->prefix_len)) || strncmp(*name, e->name, e->name_len),
-                                 LOGVAL(ctx, LY_VLOG_LINE, &context->line, LYVE_SYNTAX, "Opening and closing elements tag missmatch (\"%.*s\").", name_len, *name),
+                                 LOGVAL(ctx, LY_VLOG_LINE, &context->line, LYVE_SYNTAX, "Opening and closing elements tag missmatch (\"%.*s\").", *name_len, *name),
                                  LY_EVALID);
                 /* opening and closing element tags matches, remove record from the opening tags list */
                 free(e);
                 --context->elements.count;
+
+                /* remove also the namespaces conneted with the element */
+                lyxml_ns_rm(context);
+
                 /* do not return element information to announce closing element being currently processed */
                 *name = *prefix = NULL;
                 *name_len = *prefix_len = 0;
@@ -640,75 +826,6 @@
     return LY_SUCCESS;
 }
 
-LY_ERR
-lyxml_ns_add(struct lyxml_context *context, const char *element_name, const char *prefix, size_t prefix_len, char *uri, size_t uri_len)
-{
-    struct lyxml_ns *ns;
-
-    ns = malloc(sizeof *ns);
-    LY_CHECK_ERR_RET(!ns, LOGMEM(context->ctx), LY_EMEM);
-
-    ns->element = element_name;
-    ns->uri = strndup(uri, uri_len);
-    LY_CHECK_ERR_RET(!ns->uri, LOGMEM(context->ctx); free(ns), LY_EMEM);
-
-    if (prefix) {
-        ns->prefix = strndup(prefix, prefix_len);
-        LY_CHECK_ERR_RET(!ns->prefix, LOGMEM(context->ctx); free(ns->uri); free(ns), LY_EMEM);
-    } else {
-        ns->prefix = NULL;
-    }
-
-    LY_CHECK_ERR_RET(ly_set_add(&context->ns, ns, LY_SET_OPT_USEASLIST) == -1, free(ns->prefix); free(ns->uri); free(ns), LY_EMEM);
-    return LY_SUCCESS;
-}
-
-const struct lyxml_ns *
-lyxml_ns_get(struct lyxml_context *context, const char *prefix, size_t prefix_len)
-{
-    unsigned int u;
-    struct lyxml_ns *ns;
-
-    for (u = context->ns.count - 1; u + 1 > 0; --u) {
-        ns = (struct lyxml_ns *)context->ns.objs[u];
-        if (prefix) {
-            if (!strncmp(prefix, ns->prefix, prefix_len) && ns->prefix[prefix_len] == '\0') {
-                return ns;
-            }
-        } else if (!ns->prefix) {
-            /* default namespace */
-            return ns;
-        }
-    }
-
-    return NULL;
-}
-
-LY_ERR
-lyxml_ns_rm(struct lyxml_context *context, const char *element_name)
-{
-    unsigned int u;
-
-    for (u = context->ns.count - 1; u + 1 > 0; --u) {
-        if (((struct lyxml_ns *)context->ns.objs[u])->element != element_name) {
-            /* we are done, the namespaces from a single element are supposed to be together */
-            break;
-        }
-        /* remove the ns structure */
-        free(((struct lyxml_ns *)context->ns.objs[u])->prefix);
-        free(((struct lyxml_ns *)context->ns.objs[u])->uri);
-        free(context->ns.objs[u]);
-        --context->ns.count;
-    }
-
-    if (!context->ns.count) {
-        /* cleanup the context's namespaces storage */
-        ly_set_erase(&context->ns, NULL);
-    }
-
-    return LY_SUCCESS;
-}
-
 void
 lyxml_context_clear(struct lyxml_context *context)
 {
@@ -723,3 +840,40 @@
     }
     ly_set_erase(&context->ns, NULL);
 }
+
+LY_ERR
+lyxml_dump_text(struct lyout *out, const char *text, int attribute)
+{
+    LY_ERR ret = LY_SUCCESS;
+    unsigned int u;
+
+    if (!text) {
+        return 0;
+    }
+
+    for (u = 0; text[u]; u++) {
+        switch (text[u]) {
+        case '&':
+            ret = ly_print(out, "&amp;");
+            break;
+        case '<':
+            ret = ly_print(out, "&lt;");
+            break;
+        case '>':
+            /* not needed, just for readability */
+            ret = ly_print(out, "&gt;");
+            break;
+        case '"':
+            if (attribute) {
+                ret = ly_print(out, "&quot;");
+                break;
+            }
+            /* falls through */
+        default:
+            ly_write(out, &text[u], 1);
+        }
+    }
+
+    return ret;
+}
+
diff --git a/src/xml.h b/src/xml.h
index 5c418fd..b5e40a3 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -15,11 +15,14 @@
 #ifndef LY_XML_H_
 #define LY_XML_H_
 
+#include <stddef.h>
 #include <stdint.h>
 
-#include "context.h"
+#include "log.h"
 #include "set.h"
 
+struct lyout;
+
 /* Macro to test if character is whitespace */
 #define is_xmlws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
 
@@ -45,9 +48,9 @@
         (c >= 0x10000 && c <= 0xeffff))
 
 struct lyxml_ns {
-    const char *element;  /* element where the namespace is defined */
     char *prefix;         /* prefix of the namespace, NULL for the default namespace */
     char *uri;            /* namespace URI */
+    unsigned int depth;   /* depth level of the element to maintain the list of accessible namespace definitions */
 };
 
 /* element tag identifier for matching opening and closing tags */
@@ -80,7 +83,7 @@
 /**
  * @brief Parse input expecting an XML element.
  *
- * Able to silently skip comments, PIs and CData. DOCTYPE is not parsable, so it is reported as LY_EVALID error.
+ * Able to silently skip comments, PIs and CData. DOCTYPE is not parseable, so it is reported as LY_EVALID error.
  * If '<' is not found in input, LY_EINVAL is returned (but no error is logged), so it is possible to continue
  * with parsing input as text content.
  *
@@ -107,15 +110,19 @@
  * Input string is not being modified, so the returned values are not NULL-terminated, instead their length
  * is returned.
  *
- * In case of a namespace definition, prefix just contains xmlns string. In case of the default namespace,
- * prefix is NULL and the attribute name is xmlns.
+ * Namespace definitions are processed automatically and stored internally. To get namespace for a specific
+ * prefix, use lyxml_get_ns(). This also means, that in case there are only the namespace definitions,
+ * lyxml_get_attribute() can succeed, but nothing (name, prefix) is returned.
+ *
+ * The status member of the context is updated to provide information what the caller is supposed to call
+ * after this function.
  *
  * @param[in] context XML context to track lines or store errors into libyang context.
  * @param[in,out] input Input string to process, updated according to the processed/read data so,
  * when succeeded, it points to the opening quote of the attribute's value.
  * @param[out] prefix Pointer to prefix if present in the attribute name, NULL otherwise.
  * @param[out] prefix_len Length of the prefix if any.
- * @param[out] name Attribute name.
+ * @param[out] name Attribute name. Can be NULL only in case there is actually no attribute, but namespaces.
  * @param[out] name_len Length of the element name.
  * @return LY_ERR values.
  */
@@ -155,29 +162,6 @@
 LY_ERR lyxml_get_string(struct lyxml_context *context, const char **input, char **buffer, size_t *buffer_size, char **output, size_t *length, int *dynamic);
 
 /**
- * @brief Add namespace definition into XML context.
- *
- * Namespaces from a single element are supposed to be added sequentially together (not interleaved by a namespace from other
- * element). This mimic namespace visibility, since the namespace defined in element E is not visible from its parents or
- * siblings. On the other hand, namespace from a parent element can be redefined in a child element. This is also reflected
- * by lyxml_ns_get() which returns the most recent namespace definition for the given prefix.
- *
- * When leaving processing of a subtree of some element, caller is supposed to call lyxml_ns_rm() to remove all the namespaces
- * defined in such an element from the context.
- *
- * @param[in] context XML context to work with.
- * @param[in] element_name Pointer to the element name where the namespace is defined. Serve as an identifier to select
- * which namespaces are supposed to be removed via lyxml_ns_rm() when leaving the element's subtree.
- * @param[in] prefix Pointer to the namespace prefix as taken from lyxml_get_attribute(). Can be NULL for default namespace.
- * @param[in] prefix_len Length of the prefix string (since it is not NULL-terminated when returned from lyxml_get_attribute()).
- * @param[in] uri Namespace URI (value) to store. Value can be obtained via lyxml_get_string() and caller is not supposed to
- * work with the pointer when the function succeeds.
- * @param[in] uri_len Lenght of the URI string (since it is not NULL-terminated when returned from lyxml_get_string())
- * @return LY_ERR values.
- */
-LY_ERR lyxml_ns_add(struct lyxml_context *context, const char *element_name, const char *prefix, size_t prefix_len, char *uri, size_t uri_len);
-
-/**
  * @brief Get a namespace record for the given prefix in the current context.
  *
  * @param[in] context XML context to work with.
@@ -190,14 +174,14 @@
 const struct lyxml_ns *lyxml_ns_get(struct lyxml_context *context, const char *prefix, size_t prefix_len);
 
 /**
- * @brief Remove all the namespaces defined in the given element.
+ * @brief Print the given @p text as XML string which replaces some of the characters which cannot appear in XML data.
  *
- * @param[in] context XML context to work with.
- * @param[in] element_name Pointer to the element name where the namespaces are defined. Serve as an identifier previously provided
- * by lyxml_get_element()
+ * @param[in] out Output structure for printing.
+ * @param[in] text String to print.
+ * @param[in] attribute Flag for attribute's value where a double quotes must be replaced.
  * @return LY_ERR values.
  */
-LY_ERR lyxml_ns_rm(struct lyxml_context *context, const char *element_name);
+LY_ERR lyxml_dump_text(struct lyout *out, const char *text, int attribute);
 
 /**
  * @brief Remove the allocated working memory of the context.
diff --git a/src/xpath.c b/src/xpath.c
index 94cb734..69d656c 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -15,17 +15,13 @@
 #include "common.h"
 
 #include <ctype.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
 #include <string.h>
-#include <assert.h>
-#include <limits.h>
-#include <errno.h>
-#include <math.h>
-#include <pcre.h>
 
 #include "xpath.h"
+#include "dict.h"
 #include "xml.h"
 
 /**
@@ -306,8 +302,8 @@
                 tok_type = LYXP_TOKEN_OPERATOR_MATH;
 
             } else if (prev_function_check) {
-                LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Invalid character 0x%x, perhaps \"%.*s\" is supposed to be a function call.",
-                       expr[parsed], &expr[parsed], ret->tok_len[ret->used - 1], &ret->expr[ret->tok_pos[ret->used - 1]]);
+                LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Invalid character 0x%x ('%c'), perhaps \"%.*s\" is supposed to be a function call.",
+                       expr[parsed], expr[parsed], ret->tok_len[ret->used - 1], &ret->expr[ret->tok_pos[ret->used - 1]]);
                 goto error;
             } else {
                 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed + 1, expr);
diff --git a/src/xpath.h b/src/xpath.h
index df3a4f9..5abd427 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -17,11 +17,11 @@
 
 #include <stdint.h>
 
-#include "libyang.h"
-#include "tree_schema.h"
-#if 0
-#include "tree_data.h"
-#endif
+#include "log.h"
+
+struct ly_ctx;
+struct lysc_node;
+
 /*
  * XPath evaluator fully compliant with http://www.w3.org/TR/1999/REC-xpath-19991116/
  * except the following restrictions in the grammar.
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d79996d..01cb370 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -9,34 +9,6 @@
     find_program(VALGRIND_FOUND valgrind)
 endif()
 
-if(ENABLE_COVERAGE)
-    find_program(PATH_GCOV NAMES gcov)
-    if(NOT PATH_GCOV)
-        message(WARNING "'gcov' executable not found! Disabling building code coverage report.")
-        set(ENABLE_COVERAGE OFF)
-    endif()
-
-    find_program(PATH_LCOV NAMES lcov)
-    if(NOT PATH_GCOV)
-        message(WARNING "'lcov' executable not found! Disabling building code coverage report.")
-        set(ENABLE_COVERAGE OFF)
-    endif()
-
-    find_program(PATH_GENHTML NAMES genhtml)
-    if(NOT PATH_GCOV)
-        message(WARNING "'genhtml' executable not found! Disabling building code coverage report.")
-        set(ENABLE_COVERAGE OFF)
-    endif()
-
-    if(NOT CMAKE_COMPILER_IS_GNUCC)
-        message(WARNING "Compiler is not gcc! Coverage may break the tests!")
-    endif()
-
-    if(ENABLE_COVERAGE)
-        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
-    endif()
-endif()
-
 configure_file("${PROJECT_SOURCE_DIR}/tests/config.h.in" "${PROJECT_BINARY_DIR}/tests/config.h" ESCAPE_QUOTES @ONLY)
 include_directories(SYSTEM ${CMOCKA_INCLUDE_DIR})
 include_directories(${PROJECT_BINARY_DIR})
@@ -44,17 +16,18 @@
 set(tests)
 set(tests_wraps)
 add_subdirectory(src)
+add_subdirectory(features)
 
 foreach(test_name IN LISTS tests)
     message(STATUS ${test_name})
     string(REGEX REPLACE "[a-z]*_(.*)" "\\1" name "${test_name}")
     string(REGEX REPLACE "([a-z]*)_.*" "\\1" prefix "${test_name}")
-    add_executable(${test_name} ${prefix}/test_${name}.c)
+    add_executable(${test_name} ${prefix}/test_${name}.c $<TARGET_OBJECTS:yangobj>)
 endforeach(test_name)
 
 # Set common attributes of all tests
 foreach(test_name IN LISTS tests)
-    target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${PCRE_LIBRARIES} m)
+    target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${PCRE2_LIBRARIES} m)
     if (NOT APPLE)
         list(GET tests_wraps 0 test_wrap)
         set_target_properties(${test_name} PROPERTIES LINK_FLAGS "${test_wrap}")
@@ -88,7 +61,7 @@
     # Add coverage target
     add_custom_target(coverage
         COMMENT "Generating code coverage..."
-        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
+        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
         # Cleanup code counters
         COMMAND "${PATH_LCOV}" --directory . --zerocounters --quiet
 
@@ -104,7 +77,7 @@
             --output-file "${COVERAGE_FILE_RAW}"
         # Remove coverage of tests, system headers, etc.
         COMMAND "${PATH_LCOV}"
-            --remove "${COVERAGE_FILE_RAW}" '${CMAKE_BINARY_DIR}/*' '${CMAKE_BINARY_DIR}/tests/*' '/usr/*'
+            --remove "${COVERAGE_FILE_RAW}" '${CMAKE_SOURCE_DIR}/tests/*'
             --rc lcov_branch_coverage=1
             --quiet --output-file "${COVERAGE_FILE_CLEAN}"
         # Generate HTML report
diff --git a/tests/features/CMakeLists.txt b/tests/features/CMakeLists.txt
new file mode 100644
index 0000000..00f3423
--- /dev/null
+++ b/tests/features/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(local_tests
+    features_types)
+set(local_tests_wraps
+    " ")
+set(tests ${tests} ${local_tests} PARENT_SCOPE)
+set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
new file mode 100644
index 0000000..329c150
--- /dev/null
+++ b/tests/features/test_types.c
@@ -0,0 +1,524 @@
+/*
+ * @file test_parser_xml.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from parser_xml.c
+ *
+ * Copyright (c) 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
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../../src/libyang.h"
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+struct state_s {
+    void *func;
+    struct ly_ctx *ctx;
+};
+
+/* 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
+setup(void **state)
+{
+    struct state_s *s;
+    const char *schema_a = "module types {namespace urn:tests:types;prefix t;yang-version 1.1; feature f;"
+            "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}"
+            "leaf binary-norestr {type binary;}"
+            "leaf int8 {type int8 {range 10..20;}}"
+            "leaf uint8 {type uint8 {range 150..200;}}"
+            "leaf int16 {type int16 {range -20..-10;}}"
+            "leaf uint16 {type uint16 {range 150..200;}}"
+            "leaf int32 {type int32;}"
+            "leaf uint32 {type uint32;}"
+            "leaf int64 {type int64;}"
+            "leaf uint64 {type uint64;}"
+            "leaf bits {type bits {bit zero; bit one {if-feature f;} bit two;}}"
+            "leaf enums {type enumeration {enum white; enum yellow {if-feature f;}}}"
+            "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}"
+            "leaf dec64-norestr {type decimal64 {fraction-digits 18;}}"
+            "leaf str {type string {length 8..10; pattern '[a-z ]*';}}"
+            "leaf str-norestr {type string;}}";
+
+    s = calloc(1, sizeof *s);
+    assert_non_null(s);
+
+#if ENABLE_LOGGER_CHECKING
+    ly_set_log_clb(logger, 1);
+#endif
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &s->ctx));
+    assert_non_null(lys_parse_mem(s->ctx, schema_a, LYS_IN_YANG));
+
+    *state = s;
+
+    return 0;
+}
+
+static int
+teardown(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+
+#if ENABLE_LOGGER_CHECKING
+    if (s->func) {
+        fprintf(stderr, "%s\n", logbuf);
+    }
+#endif
+
+    ly_ctx_destroy(s->ctx, NULL);
+    free(s);
+
+    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_int(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_int;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<int8 xmlns=\"urn:tests:types\">\n 15 \t\n  </int8>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("int8", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("15", leaf->value.canonized);
+    assert_int_equal(15, leaf->value.int8);
+    lyd_free_all(tree);
+
+    /* invalid range */
+    data = "<int8 xmlns=\"urn:tests:types\">1</int8>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"1\" does not satisfy the range constraint. /");
+
+    data = "<int16 xmlns=\"urn:tests:types\">100</int16>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"100\" does not satisfy the range constraint. /");
+
+    /* invalid value */
+    data = "<int32 xmlns=\"urn:tests:types\">0x01</int32>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid int32 value \"0x01\". /");
+
+    data = "<int64 xmlns=\"urn:tests:types\"></int64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid empty int64 value. /");
+
+    data = "<int64 xmlns=\"urn:tests:types\">   </int64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid empty int64 value. /");
+
+    data = "<int64 xmlns=\"urn:tests:types\">-10  xxx</int64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid int64 value \"-10  xxx\". /");
+
+    s->func = NULL;
+}
+
+static void
+test_uint(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_uint;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<uint8 xmlns=\"urn:tests:types\">\n 150 \t\n  </uint8>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("uint8", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("150", leaf->value.canonized);
+    assert_int_equal(150, leaf->value.uint8);
+    lyd_free_all(tree);
+
+    /* invalid range */
+    data = "<uint8 xmlns=\"urn:tests:types\">\n 15 \t\n  </uint8>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"15\" does not satisfy the range constraint. /");
+
+    data = "<uint16 xmlns=\"urn:tests:types\">\n 1500 \t\n  </uint16>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"1500\" does not satisfy the range constraint. /");
+
+    /* invalid value */
+    data = "<uint32 xmlns=\"urn:tests:types\">-10</uint32>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid uint32 value \"-10\". /");
+
+    data = "<uint64 xmlns=\"urn:tests:types\"/>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid empty uint64 value. /");
+
+    data = "<uint64 xmlns=\"urn:tests:types\">   </uint64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid empty uint64 value. /");
+
+    data = "<uint64 xmlns=\"urn:tests:types\">10  xxx</uint64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid 5. character of uint64 value \"10  xxx\". /");
+
+    s->func = NULL;
+}
+
+static void
+test_dec64(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_dec64;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<dec64 xmlns=\"urn:tests:types\">\n +8 \t\n  </dec64>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("dec64", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("8.0", leaf->value.canonized);
+    assert_int_equal(80, leaf->value.dec64);
+    lyd_free_all(tree);
+
+    data = "<dec64 xmlns=\"urn:tests:types\">8.00</dec64>";
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("dec64", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("8.0", leaf->value.canonized);
+    assert_int_equal(80, leaf->value.dec64);
+    lyd_free_all(tree);
+
+    data = "<dec64-norestr xmlns=\"urn:tests:types\">-9.223372036854775808</dec64-norestr>";
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("dec64-norestr", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("-9.223372036854775808", leaf->value.canonized);
+    assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), leaf->value.dec64);
+    lyd_free_all(tree);
+
+    data = "<dec64-norestr xmlns=\"urn:tests:types\">9.223372036854775807</dec64-norestr>";
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("dec64-norestr", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("9.223372036854775807", leaf->value.canonized);
+    assert_int_equal(INT64_C(9223372036854775807), leaf->value.dec64);
+    lyd_free_all(tree);
+
+    /* invalid range */
+    data = "<dec64 xmlns=\"urn:tests:types\">\n 15 \t\n  </dec64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"15.0\" does not satisfy the range constraint. /");
+
+    data = "<dec64 xmlns=\"urn:tests:types\">\n 0 \t\n  </dec64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"0.0\" does not satisfy the range constraint. /");
+
+    /* invalid value */
+    data = "<dec64 xmlns=\"urn:tests:types\">xxx</dec64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid 1. character of decimal64 value \"xxx\". /");
+
+    data = "<dec64 xmlns=\"urn:tests:types\"/>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid empty decimal64 value. /");
+
+    data = "<dec64 xmlns=\"urn:tests:types\">   </dec64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid empty decimal64 value. /");
+
+    data = "<dec64 xmlns=\"urn:tests:types\">8.5  xxx</dec64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid 6. character of decimal64 value \"8.5  xxx\". /");
+
+    data = "<dec64 xmlns=\"urn:tests:types\">8.55  xxx</dec64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"8.55\" of decimal64 type exceeds defined number (1) of fraction digits. /");
+
+    s->func = NULL;
+}
+
+static void
+test_string(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_string;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<str xmlns=\"urn:tests:types\">teststring</str>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("str", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("teststring", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    /* invalid length */
+    data = "<str xmlns=\"urn:tests:types\">short</str>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Length \"5\" does not satisfy the length constraint. /");
+
+    data = "<str xmlns=\"urn:tests:types\">tooooo long</str>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Length \"11\" does not satisfy the length constraint. /");
+
+    /* invalid pattern */
+    data = "<str xmlns=\"urn:tests:types\">string15</str>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("String \"string15\" does not conforms to the 1. pattern restriction of its type. /");
+
+    s->func = NULL;
+}
+
+static void
+test_bits(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_bits;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<bits xmlns=\"urn:tests:types\">\n two    \t\nzero\n  </bits>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("bits", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("zero two", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    data = "<bits xmlns=\"urn:tests:types\">zero  two</bits>";
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("bits", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("zero two", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    /* disabled feature */
+    data = "<bits xmlns=\"urn:tests:types\"> \t one \n\t </bits>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Bit \"one\" is disabled by its 1. if-feature condition. /");
+
+    /* enable that feature */
+    assert_int_equal(LY_SUCCESS, lys_feature_enable(ly_ctx_get_module(s->ctx, "types", NULL), "f"));
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("bits", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("one", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    /* multiple instances of the bit */
+    data = "<bits xmlns=\"urn:tests:types\">one zero one</bits>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Bit \"one\" used multiple times. /");
+
+    /* invalid bit value */
+    data = "<bits xmlns=\"urn:tests:types\">one xero one</bits>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid bit value \"xero\". /");
+
+    s->func = NULL;
+}
+
+static void
+test_enums(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_enums;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<enums xmlns=\"urn:tests:types\">white</enums>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("enums", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("white", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    /* disabled feature */
+    data = "<enums xmlns=\"urn:tests:types\">yellow</enums>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Enumeration \"yellow\" is disabled by its 1. if-feature condition. /");
+
+    /* enable that feature */
+    assert_int_equal(LY_SUCCESS, lys_feature_enable(ly_ctx_get_module(s->ctx, "types", NULL), "f"));
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("enums", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("yellow", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    /* leading/trailing whitespaces are not valid */
+    data = "<enums xmlns=\"urn:tests:types\"> white</enums>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid enumeration value \" white\". /");
+    data = "<enums xmlns=\"urn:tests:types\">white\n</enums>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid enumeration value \"white\n\". /");
+
+    /* invalid enumeration value */
+    data = "<enums xmlns=\"urn:tests:types\">black</enums>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid enumeration value \"black\". /");
+
+    s->func = NULL;
+}
+
+static void
+test_binary(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_binary;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<binary xmlns=\"urn:tests:types\">\n   aGVs\nbG8=  \t\n  </binary>"
+                       "<binary-norestr xmlns=\"urn:tests:types\">TQ==</binary-norestr>";
+
+    /* valid data (hello) */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("binary", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("aGVs\nbG8=", leaf->value.canonized);
+    assert_non_null(tree = tree->next);
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("binary-norestr", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("TQ==", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    /* no data */
+    data = "<binary-norestr xmlns=\"urn:tests:types\">\n    \t\n  </binary-norestr>";
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("binary-norestr", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("", leaf->value.canonized);
+    lyd_free_all(tree);
+    data = "<binary-norestr xmlns=\"urn:tests:types\"></binary-norestr>";
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("binary-norestr", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("", leaf->value.canonized);
+    lyd_free_all(tree);
+    data = "<binary-norestr xmlns=\"urn:tests:types\"/>";
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("binary-norestr", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    /* invalid base64 character */
+    data = "<binary-norestr xmlns=\"urn:tests:types\">a@bcd=</binary-norestr>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid Base64 character (@). /");
+
+    /* missing data */
+    data = "<binary-norestr xmlns=\"urn:tests:types\">aGVsbG8</binary-norestr>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Base64 encoded value length must be divisible by 4. /");
+    data = "<binary-norestr xmlns=\"urn:tests:types\">VsbG8=</binary-norestr>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Base64 encoded value length must be divisible by 4. /");
+
+    /* invalid binary length */
+    data = "<binary xmlns=\"urn:tests:types\">aGVsbG93b3JsZA==</binary>"; /* helloworld */
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("This base64 value must be of length 5. /");
+    data = "<binary xmlns=\"urn:tests:types\">TQ==</binary>"; /* M */
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("This base64 value must be of length 5. /");
+
+    s->func = NULL;
+}
+
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_int, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_uint, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_dec64, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_string, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_bits, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_enums, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_binary, setup, teardown),
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt
index 5b94b7f..ed17333 100644
--- a/tests/src/CMakeLists.txt
+++ b/tests/src/CMakeLists.txt
@@ -8,7 +8,9 @@
     src_parser_yin
     src_tree_schema
     src_tree_schema_compile
-    src_tree_schema_helpers)
+    src_tree_schema_helpers
+    src_printer_yang
+    src_parser_xml)
 set(local_tests_wraps
     " "
     "-Wl,--wrap=realloc"
@@ -19,6 +21,8 @@
     " "
     " "
     " "
+    " "
+    " "
     " ")
 set(tests ${tests} ${local_tests} PARENT_SCOPE)
 set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/src/test_common.c b/tests/src/test_common.c
index 13da39b..b79f3ec 100644
--- a/tests/src/test_common.c
+++ b/tests/src/test_common.c
@@ -12,15 +12,12 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#include "../../src/common.c"
-#include "../../src/log.c"
-
 #include <stdarg.h>
 #include <stddef.h>
 #include <setjmp.h>
 #include <cmocka.h>
 
-#include "libyang.h"
+#include "../../src/common.h"
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
diff --git a/tests/src/test_context.c b/tests/src/test_context.c
index 624cf6f..78deffe 100644
--- a/tests/src/test_context.c
+++ b/tests/src/test_context.c
@@ -13,17 +13,6 @@
  */
 
 #include "tests/config.h"
-#include "../../src/common.c"
-#include "../../src/log.c"
-#include "../../src/set.c"
-#include "../../src/hash_table.c"
-#include "../../src/xpath.c"
-#include "../../src/parser_yang.c"
-#include "../../src/context.c"
-#include "../../src/tree_schema_helpers.c"
-#include "../../src/tree_schema_free.c"
-#include "../../src/tree_schema_compile.c"
-#include "../../src/tree_schema.c"
 
 #include <stdarg.h>
 #include <stddef.h>
@@ -33,7 +22,9 @@
 #include <string.h>
 #include <stdio.h>
 
-#include "libyang.h"
+#include "../../src/common.h"
+#include "../../src/context.h"
+#include "../../src/tree_schema_internal.h"
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
@@ -109,10 +100,10 @@
     logbuf_assert("Given search directory \""TESTS_BIN"/src_context\" is not a directory.");
     /* not executable */
     assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(ctx, __FILE__));
-    logbuf_assert("Unable to use search directory \""__FILE__"\" (Permission denied)");
+    logbuf_assert("Unable to fully access search directory \""__FILE__"\" (Permission denied).");
     /* not existing */
     assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(ctx, "/nonexistingfile"));
-    logbuf_assert("Unable to use search directory \"/nonexistingfile\" (No such file or directory)");
+    logbuf_assert("Unable to use search directory \"/nonexistingfile\" (No such file or directory).");
 
     /* ly_set_add() fails */
     /* no change */
@@ -174,7 +165,7 @@
 
     /* test searchdir list in ly_ctx_new() */
     assert_int_equal(LY_EINVAL, ly_ctx_new("/nonexistingfile", 0, &ctx));
-    logbuf_assert("Unable to use search directory \"/nonexistingfile\" (No such file or directory)");
+    logbuf_assert("Unable to use search directory \"/nonexistingfile\" (No such file or directory).");
     assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_SRC":/home:/home:"TESTS_SRC, 0, &ctx));
     assert_int_equal(2, ctx->search_paths.count);
     assert_string_equal(TESTS_SRC, ctx->search_paths.objs[0]);
@@ -364,6 +355,9 @@
     const char *str1 = "module a {namespace urn:a;prefix a;revision 2018-10-23;}";
     const char *str2 = "module a {namespace urn:a;prefix a;revision 2018-10-23;revision 2018-10-24;}";
 
+    unsigned int index = 0;
+    const char *names[] = {"ietf-yang-metadata", "yang", "ietf-inet-types", "ietf-yang-types", "ietf-datastores", "ietf-yang-library", "a", "a", "a"};
+
     assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
 
     /* invalid arguments */
@@ -416,6 +410,11 @@
     assert_null(lys_parse_mem_module(ctx, str1, LYS_IN_YANG, 1, NULL, NULL));
     logbuf_assert("Input data contains submodule which cannot be parsed directly without its main module.");
 
+    while ((mod = ly_ctx_get_module_iter(ctx, &index))) {
+        assert_string_equal(names[index - 1], mod->name);
+    }
+    assert_int_equal(9, index);
+
     /* cleanup */
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);
diff --git a/tests/src/test_hash_table.c b/tests/src/test_hash_table.c
index 2f10f0a..adececa 100644
--- a/tests/src/test_hash_table.c
+++ b/tests/src/test_hash_table.c
@@ -15,17 +15,6 @@
 #include "common.h"
 
 #include "tests/config.h"
-#include "../../src/common.c"
-#include "../../src/set.c"
-#include "../../src/log.c"
-#include "../../src/xpath.c"
-#include "../../src/hash_table.c"
-#include "../../src/parser_yang.c"
-#include "../../src/context.c"
-#include "../../src/tree_schema_helpers.c"
-#include "../../src/tree_schema_free.c"
-#include "../../src/tree_schema_compile.c"
-#include "../../src/tree_schema.c"
 
 #include <stdarg.h>
 #include <stddef.h>
@@ -35,7 +24,9 @@
 #include <string.h>
 #include <stdio.h>
 
-#include "libyang.h"
+#include "../../src/hash_table.h"
+
+struct ht_rec *lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx);
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
diff --git a/tests/src/test_parser_xml.c b/tests/src/test_parser_xml.c
new file mode 100644
index 0000000..9b7e79f
--- /dev/null
+++ b/tests/src/test_parser_xml.c
@@ -0,0 +1,127 @@
+/*
+ * @file test_parser_xml.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from parser_xml.c
+ *
+ * Copyright (c) 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
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../../src/context.h"
+#include "../../src/tree_data_internal.h"
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+struct ly_ctx *ctx; /* context for tests */
+
+/* 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
+setup(void **state)
+{
+    (void) state; /* unused */
+
+    const char *schema_a = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;"
+            "leaf foo { type string;}}";
+
+#if ENABLE_LOGGER_CHECKING
+    ly_set_log_clb(logger, 1);
+#endif
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
+    assert_non_null(lys_parse_mem(ctx, schema_a, LYS_IN_YANG));
+
+    return 0;
+}
+
+static int
+teardown(void **state)
+{
+#if ENABLE_LOGGER_CHECKING
+    if (*state) {
+        fprintf(stderr, "%s\n", logbuf);
+    }
+#else
+    (void) state; /* unused */
+#endif
+
+    ly_ctx_destroy(ctx, NULL);
+    ctx = NULL;
+
+    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_leaf(void **state)
+{
+    *state = test_leaf;
+
+    const char *data = "<foo xmlns=\"urn:tests:a\">foo value</foo>";
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, 0, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("foo", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("foo value", leaf->value.canonized);
+
+    lyd_free_all(tree);
+    *state = NULL;
+}
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_leaf, setup, teardown),
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/src/test_parser_yang.c b/tests/src/test_parser_yang.c
index 92e4ec8..15dcd8c 100644
--- a/tests/src/test_parser_yang.c
+++ b/tests/src/test_parser_yang.c
@@ -12,18 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#include "../../src/common.c"
-#include "../../src/set.c"
-#include "../../src/log.c"
-#include "../../src/hash_table.c"
-#include "../../src/xpath.c"
-#include "../../src/parser_yang.c"
-#include "../../src/context.c"
-#include "../../src/tree_schema_helpers.c"
-#include "../../src/tree_schema_free.c"
-#include "../../src/tree_schema_compile.c"
-#include "../../src/tree_schema.c"
-
 #include <stdarg.h>
 #include <stddef.h>
 #include <setjmp.h>
@@ -32,7 +20,51 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "libyang.h"
+#include "../../src/common.h"
+#include "../../src/tree_schema.h"
+#include "../../src/tree_schema_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);
+void lysp_ident_free(struct ly_ctx *ctx, struct lysp_ident *ident);
+void lysp_feature_free(struct ly_ctx *ctx, struct lysp_feature *feat);
+void lysp_deviation_free(struct ly_ctx *ctx, struct lysp_deviation *dev);
+void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp);
+void lysp_action_free(struct ly_ctx *ctx, struct lysp_action *action);
+void lysp_notif_free(struct ly_ctx *ctx, struct lysp_notif *notif);
+void lysp_augment_free(struct ly_ctx *ctx, struct lysp_augment *augment);
+void lysp_deviate_free(struct ly_ctx *ctx, struct lysp_deviate *d);
+void lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node);
+
+LY_ERR buf_add_char(struct ly_ctx *ctx, const char **input, size_t len, char **buf, size_t *buf_len, size_t *buf_used);
+LY_ERR buf_store_char(struct lys_parser_ctx *ctx, const char **input, enum yang_arg arg,
+                      char **word_p, size_t *word_len, char **word_b, size_t *buf_len, int need_buf);
+LY_ERR get_keyword(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword *kw, char **word_p, size_t *word_len);
+LY_ERR get_argument(struct lys_parser_ctx *ctx, const char **data, enum yang_arg arg,
+                    uint16_t *flags, char **word_p, char **word_b, size_t *word_len);
+LY_ERR skip_comment(struct lys_parser_ctx *ctx, const char **data, int comment);
+LY_ERR check_identifierchar(struct lys_parser_ctx *ctx, unsigned int c, int first, int *prefix);
+
+LY_ERR parse_action(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_action **actions);
+LY_ERR parse_any(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_augment(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_augment **augments);
+LY_ERR parse_case(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_container(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_deviate(struct lys_parser_ctx *ctx, const char **data, struct lysp_deviate **deviates);
+LY_ERR parse_deviation(struct lys_parser_ctx *ctx, const char **data, struct lysp_deviation **deviations);
+LY_ERR parse_feature(struct lys_parser_ctx *ctx, const char **data, struct lysp_feature **features);
+LY_ERR parse_grouping(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings);
+LY_ERR parse_choice(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_identity(struct lys_parser_ctx *ctx, const char **data, struct lysp_ident **identities);
+LY_ERR parse_leaf(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_leaflist(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_list(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+LY_ERR parse_maxelements(struct lys_parser_ctx *ctx, const char **data, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts);
+LY_ERR parse_minelements(struct lys_parser_ctx *ctx, const char **data, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts);
+LY_ERR parse_module(struct lys_parser_ctx *ctx, const char **data, struct lysp_module *mod);
+LY_ERR parse_notif(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_notif **notifs);
+LY_ERR parse_submodule(struct lys_parser_ctx *ctx, const char **data, struct lysp_submodule *submod);
+LY_ERR parse_uses(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
@@ -108,7 +140,7 @@
     char *buf, *p;
     size_t len, size;
     int prefix;
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     ctx.ctx = NULL;
     ctx.line = 1;
 
@@ -177,7 +209,7 @@
 {
     (void) state; /* unused */
 
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     const char *str, *p;
     char *word, *buf;
     size_t len;
@@ -185,14 +217,14 @@
     ctx.ctx = NULL;
     ctx.line = 1;
 
-    str = " // this is a text of / one * line */ comment\nargument";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
-    assert_string_equal("argument", word);
+    str = " // this is a text of / one * line */ comment\nargument;";
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
+    assert_string_equal("argument;", word);
     assert_null(buf);
     assert_int_equal(8, len);
 
-    str = "/* this is a \n * text // of / block * comment */\"arg\" + \"ume\" \n + \n \"nt\"";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    str = "/* this is a \n * text // of / block * comment */\"arg\" + \"ume\" \n + \n \"nt\";";
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_string_equal("argument", word);
     assert_ptr_equal(buf, word);
     assert_int_equal(8, len);
@@ -204,7 +236,7 @@
 
     str = p = " this is a not terminated comment x";
     assert_int_equal(LY_EVALID, skip_comment(&ctx, &str, 2));
-    logbuf_assert("Unexpected end-of-file, non-terminated comment. Line number 5.");
+    logbuf_assert("Unexpected end-of-input, non-terminated comment. Line number 5.");
     assert_true(str[0] == '\0');
 }
 
@@ -213,7 +245,7 @@
 {
     (void) state; /* unused */
 
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     const char *str;
     char *word, *buf;
     size_t len;
@@ -223,33 +255,41 @@
 
     /* missing argument */
     str = ";";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_MAYBE_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_MAYBE_STR_ARG, NULL, &word, &buf, &len));
     assert_null(word);
 
     str = "{";
-    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     logbuf_assert("Invalid character sequence \"{\", expected an argument. Line number 1.");
 
     /* invalid escape sequence */
     str = "\"\\s\"";
-    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     logbuf_assert("Double-quoted string unknown special character \'\\s\'. Line number 1.");
     str = "\'\\s\'"; /* valid, since it is not an escape sequence in single quoted string */
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_int_equal(2, len);
     assert_string_equal("\\s\'", word);
     assert_int_equal('\0', str[0]); /* input has been eaten */
 
     /* invalid character after the argument */
     str = "hello\"";
-    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     logbuf_assert("Invalid character sequence \"\"\", expected unquoted string character, optsep, semicolon or opening brace. Line number 1.");
     str = "hello}";
-    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     logbuf_assert("Invalid character sequence \"}\", expected unquoted string character, optsep, semicolon or opening brace. Line number 1.");
 
+    str = "\"\";"; /* empty identifier is not allowed */
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_IDENTIF_ARG, NULL, &word, &buf, &len));
+    logbuf_assert("Statement argument is required. Line number 1.");
+    logbuf_clean();
+    str = "\"\";"; /* empty reference identifier is not allowed */
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &len));
+    logbuf_assert("Statement argument is required. Line number 1.");
+
     str = "hello/x\t"; /* slash is not an invalid character */
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_int_equal(7, len);
     assert_string_equal("hello/x\t", word);
 
@@ -257,20 +297,20 @@
 
     /* different quoting */
     str = "hello ";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_null(buf);
     assert_int_equal(5, len);
     assert_string_equal("hello ", word);
 
     str = "hello/*comment*/\n";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_null(buf);
     assert_int_equal(5, len);
     assert_false(strncmp("hello", word, len));
 
 
     str = "\"hello\\n\\t\\\"\\\\\";";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_null(buf);
     assert_int_equal(9, len);
     assert_string_equal("hello\\n\\t\\\"\\\\\";", word);
@@ -280,7 +320,7 @@
     /* - space and tabs before newline are stripped out
      * - space and tabs after newline (indentation) are stripped out
      */
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_non_null(buf);
     assert_ptr_equal(word, buf);
     assert_int_equal(14, len);
@@ -289,7 +329,7 @@
 
     ctx.indent = 14;
     str = "\"hello\n \tworld!\"";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_non_null(buf);
     assert_ptr_equal(word, buf);
     assert_int_equal(12, len);
@@ -297,30 +337,30 @@
     free(buf);
 
     str = "\'hello\'";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_null(buf);
     assert_int_equal(5, len);
     assert_false(strncmp("hello", word, 5));
 
     str = "\"hel\"  +\t\n\"lo\"";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_ptr_equal(word, buf);
     assert_int_equal(5, len);
     assert_string_equal("hello", word);
     free(buf);
     str = "\"hel\"  +\t\nlo"; /* unquoted the second part */
-    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     logbuf_assert("Both string parts divided by '+' must be quoted. Line number 5.");
 
     str = "\'he\'\t\n+ \"llo\"";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_ptr_equal(word, buf);
     assert_int_equal(5, len);
     assert_string_equal("hello", word);
     free(buf);
 
     str = " \t\n\"he\"+\'llo\'";
-    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_SUCCESS, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     assert_ptr_equal(word, buf);
     assert_int_equal(5, len);
     assert_string_equal("hello", word);
@@ -328,7 +368,7 @@
 
     /* missing argument */
     str = ";";
-    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, &word, &buf, &len));
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     logbuf_assert("Invalid character sequence \";\", expected an argument. Line number 7.");
 }
 
@@ -337,7 +377,7 @@
 {
     (void) state; /* unused */
 
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     const char *str, *p;
     enum yang_keyword kw;
     char *word;
@@ -673,7 +713,7 @@
 {
     *state = test_minmax;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     uint16_t flags = 0;
     uint32_t value = 0;
     struct lysp_ext_instance *ext = NULL;
@@ -765,7 +805,7 @@
 }
 
 static struct lysp_module *
-mod_renew(struct ly_parser_ctx *ctx)
+mod_renew(struct lys_parser_ctx *ctx)
 {
     struct lysp_module *mod_p;
     static struct lys_module mod = {0};
@@ -791,7 +831,7 @@
 }
 
 static struct lysp_submodule *
-submod_renew(struct ly_parser_ctx *ctx, struct lysp_submodule *submod)
+submod_renew(struct lys_parser_ctx *ctx, struct lysp_submodule *submod)
 {
     lysp_submodule_free(ctx->ctx, submod);
     submod = calloc(1, sizeof *submod);
@@ -814,7 +854,7 @@
 {
     *state = test_module;
 
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     struct lysp_module *mod = NULL;
     struct lysp_submodule *submod = NULL;
     struct lys_module *m;
@@ -999,7 +1039,7 @@
     free(mod);
     m->parsed = NULL;
     assert_int_equal(LY_EVALID, yang_parse_module(&ctx, str, m));
-    logbuf_assert("Invalid character sequence \"module\", expected end-of-file. Line number 3.");
+    logbuf_assert("Trailing garbage \"module q {names...\" after module, expected end-of-input. Line number 3.");
     mod = mod_renew(&ctx);
 
     str = "prefix " SCHEMA_BEGINNING "}";
@@ -1062,7 +1102,7 @@
     lysp_submodule_free(ctx.ctx, submod);
     submod = NULL;
     assert_int_equal(LY_EVALID, yang_parse_submodule(&ctx, str, &submod));
-    logbuf_assert("Invalid character sequence \"module\", expected end-of-file. Line number 3.");
+    logbuf_assert("Trailing garbage \"module q {names...\" after submodule, expected end-of-input. Line number 3.");
 
     str = "prefix " SCHEMA_BEGINNING "}";
     assert_int_equal(LY_EVALID, yang_parse_submodule(&ctx, str, &submod));
@@ -1086,7 +1126,7 @@
 {
     *state = test_identity;
 
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     struct lysp_ident *ident = NULL;
     const char *str;
 
@@ -1131,7 +1171,7 @@
 {
     (void) state; /* unused */
 
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     struct lysp_feature *features = NULL;
     const char *str;
 
@@ -1174,7 +1214,7 @@
 {
     (void) state; /* unused */
 
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     struct lysp_deviation *d = NULL;
     const char *str;
 
@@ -1223,7 +1263,7 @@
 {
     (void) state; /* unused */
 
-    struct ly_parser_ctx ctx;
+    struct lys_parser_ctx ctx;
     struct lysp_deviate *d = NULL;
     const char *str;
 
@@ -1308,7 +1348,7 @@
 {
     (void) state; /* unused */
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_node_container *c = NULL;
     const char *str;
 
@@ -1381,7 +1421,7 @@
 {
     *state = test_leaf;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_node_leaf *l = NULL;
     const char *str;
 
@@ -1459,7 +1499,7 @@
 {
     *state = test_leaf;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_node_leaflist *ll = NULL;
     const char *str;
 
@@ -1557,7 +1597,7 @@
 {
     *state = test_list;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_node_list *l = NULL;
     const char *str;
 
@@ -1627,7 +1667,7 @@
 {
     *state = test_choice;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_node_choice *ch = NULL;
     const char *str;
 
@@ -1694,7 +1734,7 @@
 {
     *state = test_case;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_node_case *cs = NULL;
     const char *str;
 
@@ -1748,7 +1788,7 @@
 {
     *state = test_any;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_node_anydata *any = NULL;
     const char *str;
 
@@ -1814,7 +1854,7 @@
 {
     *state = test_grouping;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_grp *grp = NULL;
     const char *str;
 
@@ -1870,7 +1910,7 @@
 {
     *state = test_action;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_action *rpcs = NULL;
     struct lysp_node_container *c = NULL;
     const char *str;
@@ -1914,14 +1954,14 @@
     assert_non_null(rpcs->typedefs);
     assert_int_equal(LYS_STATUS_CURR, rpcs->flags);
     /* input */
-    assert_int_equal(rpcs->input.nodetype, LYS_INOUT);
+    assert_int_equal(rpcs->input.nodetype, LYS_INPUT);
     assert_non_null(rpcs->input.groupings);
     assert_non_null(rpcs->input.exts);
     assert_non_null(rpcs->input.musts);
     assert_non_null(rpcs->input.typedefs);
     assert_non_null(rpcs->input.data);
     /* output */
-    assert_int_equal(rpcs->output.nodetype, LYS_INOUT);
+    assert_int_equal(rpcs->output.nodetype, LYS_OUTPUT);
     assert_non_null(rpcs->output.groupings);
     assert_non_null(rpcs->output.exts);
     assert_non_null(rpcs->output.musts);
@@ -1943,11 +1983,71 @@
 }
 
 static void
+test_notification(void **state)
+{
+    *state = test_notification;
+
+    struct lys_parser_ctx ctx = {0};
+    struct lysp_notif *notifs = NULL;
+    struct lysp_node_container *c = NULL;
+    const char *str;
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx.ctx));
+    assert_non_null(ctx.ctx);
+    ctx.line = 1;
+    ctx.mod_version = 2; /* simulate YANG 1.1 */
+
+    /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+    str = "func {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+    assert_int_equal(LY_EVALID, parse_notif(&ctx, &str, NULL, &notifs)); \
+    logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+    FREE_ARRAY(ctx.ctx, notifs, lysp_notif_free); notifs = NULL;
+
+    TEST_DUP("description", "text1", "text2");
+    TEST_DUP("reference", "1", "2");
+    TEST_DUP("status", "current", "obsolete");
+#undef TEST_DUP
+
+    /* full content */
+    str = "top;";
+    assert_int_equal(LY_SUCCESS, parse_container(&ctx, &str, NULL, (struct lysp_node**)&c));
+    str = "ntf {anydata a1; anyxml a2; choice ch; container c; description test; grouping grp; if-feature f; leaf l {type int8;}"
+          "leaf-list ll {type int8;} list li; must 1; reference test; status current; typedef mytype {type int8;} uses grp; m:ext;}";
+    assert_int_equal(LY_SUCCESS, parse_notif(&ctx, &str, (struct lysp_node*)c, &notifs));
+    assert_non_null(notifs);
+    assert_int_equal(LYS_NOTIF, notifs->nodetype);
+    assert_string_equal("ntf", notifs->name);
+    assert_string_equal("test", notifs->dsc);
+    assert_non_null(notifs->exts);
+    assert_non_null(notifs->iffeatures);
+    assert_string_equal("test", notifs->ref);
+    assert_non_null(notifs->groupings);
+    assert_non_null(notifs->typedefs);
+    assert_non_null(notifs->musts);
+    assert_non_null(notifs->data);
+    assert_int_equal(LYS_STATUS_CURR, notifs->flags);
+
+    ly_set_erase(&ctx.tpdfs_nodes, NULL);
+    FREE_ARRAY(ctx.ctx, notifs, lysp_notif_free); notifs = NULL;
+
+    /* invalid content */
+    str = "ntf {config true} ...";
+    assert_int_equal(LY_EVALID, parse_notif(&ctx, &str, NULL, &notifs));
+    logbuf_assert("Invalid keyword \"config\" as a child of \"notification\". Line number 1.");
+    FREE_ARRAY(ctx.ctx, notifs, lysp_notif_free); notifs = NULL;
+
+    *state = NULL;
+    lysp_node_free(ctx.ctx, (struct lysp_node*)c);
+    ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
 test_uses(void **state)
 {
     *state = test_uses;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_node_uses *u = NULL;
     const char *str;
 
@@ -1997,7 +2097,7 @@
 {
     *state = test_augment;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lysp_augment *a = NULL;
     const char *str;
 
@@ -2061,6 +2161,7 @@
         cmocka_unit_test_setup_teardown(test_anydata, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_anyxml, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_action, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_notification, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_grouping, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_uses, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_augment, logger_setup, logger_teardown),
diff --git a/tests/src/test_parser_yin.c b/tests/src/test_parser_yin.c
index deb6645..de3e1d4 100644
--- a/tests/src/test_parser_yin.c
+++ b/tests/src/test_parser_yin.c
@@ -1,17 +1,3 @@
-#include "../../src/common.c"
-#include "../../src/set.c"
-#include "../../src/log.c"
-#include "../../src/hash_table.c"
-#include "../../src/xpath.c"
-#include "../../src/parser_yang.c"
-#include "../../src/context.c"
-#include "../../src/tree_schema_helpers.c"
-#include "../../src/tree_schema_free.c"
-#include "../../src/tree_schema_compile.c"
-#include "../../src/tree_schema.c"
-#include "../../src/xml.c"
-#include "../../src/parser_yin.c"
-
 #include <stdarg.h>
 #include <stddef.h>
 #include <setjmp.h>
@@ -20,7 +6,10 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "libyang.h"
+#include "../../src/common.h"
+#include "../../src/tree_schema.h"
+#include "../../src/tree_schema_internal.h"
+#include "../../src/parser_yin.h"
 
 struct state {
     struct ly_ctx *ctx;
diff --git a/tests/src/test_printer_yang.c b/tests/src/test_printer_yang.c
new file mode 100644
index 0000000..1f3f749
--- /dev/null
+++ b/tests/src/test_printer_yang.c
@@ -0,0 +1,232 @@
+/*
+ * @file test_printer_yang.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from printer_yang.c
+ *
+ * Copyright (c) 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
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../../src/context.h"
+#include "../../src/printer_schema.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 */
+#if ENABLE_LOGGER_CHECKING
+    ly_set_log_clb(logger, 1);
+#endif
+    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_module(void **state)
+{
+    *state = test_module;
+
+    struct ly_ctx *ctx = {0};
+    const struct lys_module *mod;
+    const char *orig = "module a {\n"
+            "  yang-version 1.1;\n"
+            "  namespace \"urn:test:a\";\n"
+            "  prefix a;\n\n"
+            "  import ietf-yang-types {\n"
+            "    prefix yt;\n"
+            "    revision-date 2013-07-15;\n"
+            "    description\n"
+            "      \"YANG types\";\n"
+            "    reference\n"
+            "      \"RFC reference\";\n"
+            "  }\n\n"
+            "  organization\n"
+            "    \"ORG\";\n"
+            "  contact\n"
+            "    \"Radek Krejci.\";\n"
+            "  description\n"
+            "    \"Long multiline\n"
+            "      description.\";\n"
+            "  reference\n"
+            "    \"some reference\";\n"
+            "}\n";
+    char *compiled = "module a {\n"
+            "  yang-version 1.1;\n"
+            "  namespace \"urn:test:a\";\n"
+            "  prefix a;\n\n"
+            "  import ietf-yang-types {\n"
+            "    prefix yt;\n"
+            "    revision-date 2013-07-15;\n"
+            "  }\n\n"
+            "  organization\n"
+            "    \"ORG\";\n"
+            "  contact\n"
+            "    \"Radek Krejci.\";\n"
+            "  description\n"
+            "    \"Long multiline\n"
+            "      description.\";\n"
+            "  reference\n"
+            "    \"some reference\";\n"
+            "}\n";
+    char *printed;
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
+
+    assert_non_null(mod = lys_parse_mem(ctx, orig, LYS_IN_YANG));
+    assert_int_equal(strlen(orig), lys_print_mem(&printed, mod, LYS_OUT_YANG, 0, 0));
+    assert_string_equal(printed, orig);
+    free(printed);
+    assert_int_equal(strlen(compiled), lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0, 0));
+    assert_string_equal(printed, compiled);
+    free(printed);
+
+    orig = "module b {\n"
+            "  yang-version 1.1;\n"
+            "  namespace \"urn:test:b\";\n"
+            "  prefix b;\n\n"
+            "  revision 2019-04-16 {\n"
+            "    description\n"
+            "      \"text\";\n"
+            "    reference\n"
+            "      \"text\";\n"
+            "  }\n"
+            "  revision 2019-04-15 {\n"
+            "    description\n"
+            "      \"initial revision\";\n"
+            "  }\n\n"
+            "  feature f1 {\n"
+            "    status current;\n"
+            "    description\n"
+            "      \"text\";\n"
+            "    reference\n"
+            "      \"text\";\n"
+            "  }\n\n"
+            "  feature f2 {\n"
+            "    if-feature \"not f1\";\n"
+            "  }\n"
+            "}\n";
+    compiled = "module b {\n"
+            "  yang-version 1.1;\n"
+            "  namespace \"urn:test:b\";\n"
+            "  prefix b;\n\n"
+            "  revision 2019-04-16;\n\n"
+            "  feature f1 {\n"
+            "    status current;\n"
+            "    description\n"
+            "      \"text\";\n"
+            "    reference\n"
+            "      \"text\";\n"
+            "  }\n\n"
+            "  feature f2 {\n"
+            "    if-feature \"not f1\";\n"
+            "  }\n"
+            "}\n";
+    assert_non_null(mod = lys_parse_mem(ctx, orig, LYS_IN_YANG));
+    assert_int_equal(strlen(orig), lys_print_mem(&printed, mod, LYS_OUT_YANG, 0, 0));
+    assert_string_equal(printed, orig);
+    free(printed);
+    assert_int_equal(strlen(compiled), lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0, 0));
+    assert_string_equal(printed, compiled);
+    free(printed);
+
+    orig = compiled ="module c {\n"
+            "  yang-version 1.1;\n"
+            "  namespace \"urn:test:c\";\n"
+            "  prefix c;\n\n"
+            "  feature f1;\n\n"
+            "  identity i1 {\n"
+            "    if-feature \"f1\";\n"
+            "    description\n"
+            "      \"text\";\n"
+            "    reference\n"
+            "      \"text32\";\n"
+            "  }\n\n"
+            "  identity i2 {\n"
+            "    base i1;\n"
+            "    status obsolete;\n"
+            "  }\n"
+            "}\n";
+    assert_non_null(mod = lys_parse_mem(ctx, orig, LYS_IN_YANG));
+    assert_int_equal(strlen(orig), lys_print_mem(&printed, mod, LYS_OUT_YANG, 0, 0));
+    assert_string_equal(printed, orig);
+    free(printed);
+    assert_int_equal(strlen(compiled), lys_print_mem(&printed, mod, LYS_OUT_YANG, 0, 0));
+    assert_string_equal(printed, compiled);
+    free(printed);
+
+    *state = NULL;
+    ly_ctx_destroy(ctx, NULL);
+}
+
+/* TODO: include */
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_module, logger_setup, logger_teardown),
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/src/test_set.c b/tests/src/test_set.c
index 77c9c0d..3e288b5 100644
--- a/tests/src/test_set.c
+++ b/tests/src/test_set.c
@@ -20,9 +20,7 @@
 
 #include <string.h>
 
-#include "libyang.h"
-#include "../../src/set.c"
-#include "../../src/log.c"
+#include "../../src/set.h"
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
@@ -102,13 +100,13 @@
     memset(&set, 0, sizeof set);
 
     ly_set_clean(NULL, NULL);
-    assert_string_equal(logbuf, "Invalid argument set (ly_set_clean()).");
+    assert_string_equal(logbuf, "");
 
     ly_set_erase(NULL, NULL);
-    assert_string_equal(logbuf, "Invalid argument set (ly_set_erase()).");
+    assert_string_equal(logbuf, "");
 
     ly_set_free(NULL, NULL);
-    assert_string_equal(logbuf, "Invalid argument set (ly_set_free()).");
+    assert_string_equal(logbuf, "");
 
     assert_null(ly_set_dup(NULL, NULL));
     assert_string_equal(logbuf, "Invalid argument set (ly_set_dup()).");
diff --git a/tests/src/test_tree_schema.c b/tests/src/test_tree_schema.c
index 0a91f03..bcee581 100644
--- a/tests/src/test_tree_schema.c
+++ b/tests/src/test_tree_schema.c
@@ -12,25 +12,14 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#include "../../src/common.c"
-#include "../../src/log.c"
-#include "../../src/set.c"
-#include "../../src/parser_yang.c"
-#include "../../src/tree_schema.c"
-#include "../../src/tree_schema_compile.c"
-#include "../../src/tree_schema_free.c"
-#include "../../src/tree_schema_helpers.c"
-#include "../../src/hash_table.c"
-#include "../../src/xpath.c"
-#include "../../src/context.c"
-
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <setjmp.h>
 #include <cmocka.h>
 
-#include "libyang.h"
+#include "../../src/common.h"
+#include "../../src/tree_schema.h"
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
@@ -135,12 +124,10 @@
     rpc = (const struct lysc_action*)node;
     assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
     assert_string_equal("i", node->name);
-    /* TODO Notifications
     assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
     assert_string_equal("j", node->name);
     assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
     assert_string_equal("k", node->name);
-    */
     assert_null(node = lys_getnext(node, NULL, mod->compiled, 0));
     /* Inside container */
     assert_non_null(node = lys_getnext(node, (const struct lysc_node*)cont, mod->compiled, 0));
@@ -159,10 +146,8 @@
     assert_string_equal("seven", node->name);
     assert_non_null(node = lys_getnext(node, (const struct lysc_node*)cont, mod->compiled, 0));
     assert_string_equal("eight", node->name);
-    /* TODO Notifications
     assert_non_null(node = lys_getnext(node, (const struct lysc_node*)cont, mod->compiled, 0));
     assert_string_equal("nine", node->name);
-    */
     assert_null(node = lys_getnext(node, (const struct lysc_node*)cont, mod->compiled, 0));
     /* Inside RPC */
     assert_non_null(node = lys_getnext(node, (const struct lysc_node*)rpc, mod->compiled, 0));
@@ -200,6 +185,15 @@
     assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, LYS_GETNEXT_NOSTATECHECK));
     assert_string_equal("a", node->name);
 
+    assert_non_null(mod = lys_parse_mem(ctx, "module c {namespace urn:c;prefix c; rpc c;}", LYS_IN_YANG));
+    assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
+    assert_string_equal("c", node->name);
+    assert_null(node = lys_getnext(node, NULL, mod->compiled, LYS_GETNEXT_NOSTATECHECK));
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module d {namespace urn:d;prefix d; notification d;}", LYS_IN_YANG));
+    assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
+    assert_string_equal("d", node->name);
+    assert_null(node = lys_getnext(node, NULL, mod->compiled, LYS_GETNEXT_NOSTATECHECK));
 
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 8f50ead..3cff524 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -12,18 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#include "../../src/common.c"
-#include "../../src/log.c"
-#include "../../src/set.c"
-#include "../../src/xpath.c"
-#include "../../src/parser_yang.c"
-#include "../../src/tree_schema_helpers.c"
-#include "../../src/tree_schema_free.c"
-#include "../../src/tree_schema_compile.c"
-#include "../../src/tree_schema.c"
-#include "../../src/context.c"
-#include "../../src/hash_table.c"
-
 #include <stdarg.h>
 #include <stddef.h>
 #include <setjmp.h>
@@ -32,7 +20,14 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "libyang.h"
+#include "../../src/common.h"
+#include "../../src/tree_schema_internal.h"
+#include "../../src/xpath.h"
+
+void lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat);
+
+LY_ERR lys_path_token(const char **path, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
+                      int *parent_times, int *has_predicate);
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
@@ -125,7 +120,7 @@
     *state = test_module;
 
     const char *str;
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lys_module mod = {0};
     struct lysc_feature *f;
     struct lysc_iffeature *iff;
@@ -193,7 +188,7 @@
 {
     *state = test_feature;
 
-    struct ly_parser_ctx ctx = {0};
+    struct lys_parser_ctx ctx = {0};
     struct lys_module mod = {0}, *modp;
     const char *str;
     struct lysc_feature *f, *f1;
@@ -521,15 +516,24 @@
     assert_string_equal("10", ll->dflts[0]);
     assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, ll->flags);
 
-    /* ordered-by is ignored for state data, RPC/action output parameters and notification content
-     * TODO test also, RPC output parameters and notification content */
+    /* ordered-by is ignored for state data, RPC/action output parameters and notification content */
     assert_non_null(mod = lys_parse_mem(ctx, "module d {yang-version 1.1;namespace urn:d;prefix d;"
                                         "leaf-list ll {config false; type string; ordered-by user;}}", LYS_IN_YANG));
     /* but warning is present: */
-    logbuf_assert("The ordered-by statement is ignored in lists representing state data, RPC/action output parameters or notification content ().");
+    logbuf_assert("The ordered-by statement is ignored in lists representing state data ().");
     assert_non_null(mod->compiled);
     assert_non_null((ll = (struct lysc_node_leaflist*)mod->compiled->data));
     assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM | LYS_SET_CONFIG, ll->flags);
+    logbuf_clean();
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module e {yang-version 1.1;namespace urn:e;prefix e;"
+                                        "rpc oper {output {leaf-list ll {type string; ordered-by user;}}}}", LYS_IN_YANG));
+    logbuf_assert("The ordered-by statement is ignored in lists representing RPC/action output parameters ().");
+    logbuf_clean();
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module f {yang-version 1.1;namespace urn:f;prefix f;"
+                                        "notification event {leaf-list ll {type string; ordered-by user;}}}", LYS_IN_YANG));
+    logbuf_assert("The ordered-by statement is ignored in lists representing notification content ().");
 
     /* invalid */
     assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;leaf-list ll {type empty;}}", LYS_IN_YANG));
@@ -817,10 +821,93 @@
     logbuf_assert("Duplicate identifier \"y\" of data definition/RPC/action/Notification statement.");
     assert_null(lys_parse_mem(ctx, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {action z; action z;}}", LYS_IN_YANG));
     logbuf_assert("Duplicate identifier \"z\" of data definition/RPC/action/Notification statement.");
-    ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} rpc w;}");
+    ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} notification w;}");
     assert_null(lys_parse_mem(ctx, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; rpc w;}", LYS_IN_YANG));
     logbuf_assert("Duplicate identifier \"w\" of data definition/RPC/action/Notification statement.");
 
+    assert_null(lys_parse_mem(ctx, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}"
+                              "augment /test/input/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG));
+    logbuf_assert("Action \"invalid\" is placed inside another RPC/action.");
+
+    assert_null(lys_parse_mem(ctx, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}"
+                              "augment /test/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG));
+    logbuf_assert("Action \"invalid\" is placed inside Notification.");
+
+    assert_null(lys_parse_mem(ctx, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; notification test {container a {uses grp;}}"
+                              "grouping grp {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG));
+    logbuf_assert("Action \"invalid\" is placed inside Notification.");
+
+    *state = NULL;
+    ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_notification(void **state)
+{
+    *state = test_notification;
+
+    struct ly_ctx *ctx;
+    struct lys_module *mod;
+    const struct lysc_notif *notif;
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;"
+                                        "notification a1 {leaf x {type int8;}} notification a2;}", LYS_IN_YANG));
+    notif = mod->compiled->notifs;
+    assert_non_null(notif);
+    assert_int_equal(2, LY_ARRAY_SIZE(notif));
+    assert_int_equal(LYS_NOTIF, notif->nodetype);
+    assert_int_equal(LYS_STATUS_CURR, notif->flags);
+    assert_string_equal("a1", notif->name);
+    assert_non_null(notif->data);
+    assert_string_equal("x", notif->data->name);
+    assert_int_equal(LYS_NOTIF, notif[1].nodetype);
+    assert_int_equal(LYS_STATUS_CURR, notif[1].flags);
+    assert_string_equal("a2", notif[1].name);
+    assert_null(notif[1].data);
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module b {yang-version 1.1; namespace urn:b;prefix b; container top {"
+                                        "notification b1 {leaf x {type int8;}} notification b2;}}", LYS_IN_YANG));
+    notif = lysc_node_notifs(mod->compiled->data);
+    assert_non_null(notif);
+    assert_int_equal(2, LY_ARRAY_SIZE(notif));
+    assert_int_equal(LYS_NOTIF, notif->nodetype);
+    assert_int_equal(LYS_STATUS_CURR, notif->flags);
+    assert_string_equal("b1", notif->name);
+    assert_non_null(notif->data);
+    assert_string_equal("x", notif->data->name);
+    assert_int_equal(LYS_NOTIF, notif[1].nodetype);
+    assert_int_equal(LYS_STATUS_CURR, notif[1].flags);
+    assert_string_equal("b2", notif[1].name);
+    assert_null(notif[1].data);
+
+    /* invalid */
+    assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;container top {notification x;}}", LYS_IN_YANG));
+    logbuf_assert("Invalid keyword \"notification\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules. Line number 1.");
+
+    assert_null(lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} notification x;}", LYS_IN_YANG));
+    logbuf_assert("Duplicate identifier \"x\" of data definition/RPC/action/Notification statement.");
+    assert_null(lys_parse_mem(ctx, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} notification y;}}", LYS_IN_YANG));
+    logbuf_assert("Duplicate identifier \"y\" of data definition/RPC/action/Notification statement.");
+    assert_null(lys_parse_mem(ctx, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {notification z; notification z;}}", LYS_IN_YANG));
+    logbuf_assert("Duplicate identifier \"z\" of data definition/RPC/action/Notification statement.");
+    ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} rpc w;}");
+    assert_null(lys_parse_mem(ctx, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; notification w;}", LYS_IN_YANG));
+    logbuf_assert("Duplicate identifier \"w\" of data definition/RPC/action/Notification statement.");
+
+    assert_null(lys_parse_mem(ctx, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}"
+                              "augment /test/input/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG));
+    logbuf_assert("Notification \"invalid\" is placed inside RPC/action.");
+
+    assert_null(lys_parse_mem(ctx, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}"
+                              "augment /test/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG));
+    logbuf_assert("Notification \"invalid\" is placed inside another Notification.");
+
+    assert_null(lys_parse_mem(ctx, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; rpc test {input {container a {uses grp;}}}"
+                              "grouping grp {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG));
+    logbuf_assert("Notification \"invalid\" is placed inside RPC/action.");
+
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);
 }
@@ -1195,9 +1282,11 @@
     assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->patterns));
     assert_string_equal("errortag", ((struct lysc_type_str*)type)->patterns[0]->eapptag);
     assert_string_equal("error", ((struct lysc_type_str*)type)->patterns[0]->emsg);
+    assert_string_equal(".*", ((struct lysc_type_str*)type)->patterns[0]->expr);
     assert_int_equal(0, ((struct lysc_type_str*)type)->patterns[0]->inverted);
     assert_null(((struct lysc_type_str*)type)->patterns[1]->eapptag);
     assert_null(((struct lysc_type_str*)type)->patterns[1]->emsg);
+    assert_string_equal("[0-9].*[0-9]", ((struct lysc_type_str*)type)->patterns[1]->expr);
     assert_int_equal(1, ((struct lysc_type_str*)type)->patterns[1]->inverted);
 
     assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;typedef mytype {type string {pattern '[0-9]*';}}"
@@ -1208,7 +1297,9 @@
     assert_int_equal(1, type->refcount);
     assert_non_null(((struct lysc_type_str*)type)->patterns);
     assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->patterns));
+    assert_string_equal("[0-9]*", ((struct lysc_type_str*)type)->patterns[0]->expr);
     assert_int_equal(3, ((struct lysc_type_str*)type)->patterns[0]->refcount);
+    assert_string_equal("[0-4]*", ((struct lysc_type_str*)type)->patterns[1]->expr);
     assert_int_equal(1, ((struct lysc_type_str*)type)->patterns[1]->refcount);
 
     assert_non_null(mod = lys_parse_mem(ctx, "module c {namespace urn:c;prefix c;typedef mytype {type string {pattern '[0-9]*';}}"
@@ -1219,6 +1310,7 @@
     assert_int_equal(1, type->refcount);
     assert_non_null(((struct lysc_type_str*)type)->patterns);
     assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->patterns));
+    assert_string_equal("[0-9]*", ((struct lysc_type_str*)type)->patterns[0]->expr);
     assert_int_equal(2, ((struct lysc_type_str*)type)->patterns[0]->refcount);
 
     /* test substitutions */
@@ -1228,6 +1320,7 @@
     assert_non_null(type);
     assert_non_null(((struct lysc_type_str*)type)->patterns);
     assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->patterns));
+    assert_string_equal("^\\p{IsLatinExtended-A}$", ((struct lysc_type_str*)type)->patterns[0]->expr);
     /* TODO check some data "^ř$" */
 
     *state = NULL;
@@ -1813,6 +1906,20 @@
     assert_non_null(mod->compiled->data);
     assert_string_equal("h", mod->compiled->data->name);
 
+    ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "module j {namespace urn:j;prefix j; leaf j  {type string;}}");
+    assert_non_null(mod = lys_parse_mem(ctx, "module k {namespace urn:k;prefix k;import j {prefix j;}"
+                                        "leaf i {type leafref {path \"/ilist[name = current()/../j:j]/value\";}}"
+                                        "list ilist {key name; leaf name {type string;} leaf value {type uint16;}}}", LYS_IN_YANG));
+    type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+    assert_non_null(type);
+    assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+    assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+    assert_int_equal(LY_TYPE_UINT16, ((struct lysc_type_leafref*)type)->realtype->basetype);
+    assert_non_null(mod = ly_ctx_get_module_implemented(ctx, "j"));
+    assert_int_equal(1, mod->implemented);
+    assert_non_null(mod->compiled->data);
+    assert_string_equal("j", mod->compiled->data->name);
+
     /* invalid paths */
     assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;container a {leaf target2 {type uint8;}}"
                                         "leaf ref1 {type leafref {path ../a/invalid;}}}", LYS_IN_YANG));
@@ -1938,21 +2045,21 @@
                                         "leaf ifname{type leafref{ path \"../interface/name\";}}"
                                         "leaf address {type leafref{ path \"/interface[name=current()/../x:ifname]/ip\";}}}",
                                         LYS_IN_YANG));
-    logbuf_assert("Invalid leafref path predicate \"[name=current()/../x:ifname]\" - unable to find module of the node \"ifname\" in rel-path_keyexpr.");
+    logbuf_assert("Invalid leafref path predicate \"[name=current()/../x:ifname]\" - unable to find module of the node \"ifname\" in rel-path-keyexpr.");
 
     assert_null(lys_parse_mem(ctx, "module zz {namespace urn:zz;prefix zz;"
                                         "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
                                         "leaf ifname{type leafref{ path \"../interface/name\";}}"
                                         "leaf address {type leafref{ path \"/interface[name=current()/../xxx]/ip\";}}}",
                                         LYS_IN_YANG));
-    logbuf_assert("Invalid leafref path predicate \"[name=current()/../xxx]\" - unable to find node \"current()/../xxx\" in the rel-path_keyexpr.");
+    logbuf_assert("Invalid leafref path predicate \"[name=current()/../xxx]\" - unable to find node \"current()/../xxx\" in the rel-path-keyexpr.");
 
     assert_null(lys_parse_mem(ctx, "module zza {namespace urn:zza;prefix zza;"
                                         "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
                                         "leaf ifname{type leafref{ path \"../interface/name\";}}container c;"
                                         "leaf address {type leafref{ path \"/interface[name=current()/../c]/ip\";}}}",
                                         LYS_IN_YANG));
-    logbuf_assert("Invalid leafref path predicate \"[name=current()/../c]\" - rel-path_keyexpr \"current()/../c\" refers container instead of leaf.");
+    logbuf_assert("Invalid leafref path predicate \"[name=current()/../c]\" - rel-path-keyexpr \"current()/../c\" refers container instead of leaf.");
 
     assert_null(lys_parse_mem(ctx, "module zzb {namespace urn:zzb;prefix zzb;"
                                         "list interface{key name;leaf name{type string;}leaf ip {type string;}container c;}"
@@ -2065,6 +2172,13 @@
                                         "typedef mytype2 {type mytype {type string;}}leaf l {type mytype2;}}", LYS_IN_YANG));
     logbuf_assert("Invalid type substatement for the type \"mytype2\" not directly derived from union built-in type.");
 
+    assert_null(lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;typedef mytype {type union{type mytype; type string;}}"
+                                        "leaf l {type mytype;}}", LYS_IN_YANG));
+    logbuf_assert("Invalid \"mytype\" type reference - circular chain of types detected.");
+    assert_null(lys_parse_mem(ctx, "module ef {namespace urn:ef;prefix ef;typedef mytype {type mytype2;}"
+                                        "typedef mytype2 {type mytype;} leaf l {type mytype;}}", LYS_IN_YANG));
+    logbuf_assert("Invalid \"mytype\" type reference - circular chain of types detected.");
+
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);
 }
@@ -2176,6 +2290,42 @@
 }
 
 static void
+test_grouping(void **state)
+{
+    *state = test_grouping;
+
+    struct ly_ctx *ctx;
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+    /* result ok, but a warning about not used locally scoped grouping printed */
+    assert_non_null(lys_parse_mem(ctx, "module a {namespace urn:a;prefix a; grouping grp1 {leaf a1 {type string;}}"
+                                  "container a {leaf x {type string;} grouping grp2 {leaf a2 {type string;}}}}", LYS_IN_YANG));
+    logbuf_assert("Locally scoped grouping \"grp2\" not used.");
+    logbuf_clean();
+
+    /* result ok - when statement or leafref target must be checked only at the place where the grouping is really instantiated */
+    assert_non_null(lys_parse_mem(ctx, "module b {namespace urn:b;prefix b; grouping grp {"
+                                  "leaf ref {type leafref {path \"../name\";}}"
+                                  "leaf cond {type string; when \"../name = 'specialone'\";}}}", LYS_IN_YANG));
+    logbuf_assert("");
+
+
+    /* invalid - error in a non-instantiated grouping */
+    assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;"
+                                        "grouping grp {leaf x {type leafref;}}}", LYS_IN_YANG));
+    logbuf_assert("Missing path substatement for leafref type.");
+    logbuf_clean();
+    assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;"
+                                        "container a {grouping grp {leaf x {type leafref;}}}}", LYS_IN_YANG));
+    logbuf_assert("Missing path substatement for leafref type.");
+
+
+    *state = NULL;
+    ly_ctx_destroy(ctx, NULL);
+}
+
+static void
 test_uses(void **state)
 {
     *state = test_uses;
@@ -2183,6 +2333,7 @@
     struct ly_ctx *ctx;
     struct lys_module *mod;
     const struct lysc_node *parent, *child;
+    const struct lysc_node_container *cont;
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
 
@@ -2246,6 +2397,30 @@
     assert_non_null(child = lysc_node_children(child, 0));
     assert_string_equal("x", child->name);
 
+    assert_non_null(mod = lys_parse_mem(ctx, "module e {yang-version 1.1;namespace urn:e;prefix e; grouping grp {action g { description \"super g\";}}"
+                                        "container top {action e; uses grp {refine g {description \"ultra g\";}}}}", LYS_IN_YANG));
+    assert_non_null(mod->compiled->data);
+    cont = (const struct lysc_node_container*)mod->compiled->data;
+    assert_non_null(cont->actions);
+    assert_int_equal(2, LY_ARRAY_SIZE(cont->actions));
+    assert_string_equal("e", cont->actions[1].name);
+    assert_string_equal("g", cont->actions[0].name);
+    assert_string_equal("ultra g", cont->actions[0].dsc);
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module f {yang-version 1.1;namespace urn:f;prefix f; grouping grp {notification g { description \"super g\";}}"
+                                        "container top {notification f; uses grp {refine g {description \"ultra g\";}}}}", LYS_IN_YANG));
+    assert_non_null(mod->compiled->data);
+    cont = (const struct lysc_node_container*)mod->compiled->data;
+    assert_non_null(cont->notifs);
+    assert_int_equal(2, LY_ARRAY_SIZE(cont->notifs));
+    assert_string_equal("f", cont->notifs[1].name);
+    assert_string_equal("g", cont->notifs[0].name);
+    assert_string_equal("ultra g", cont->notifs[0].dsc);
+
+    /* empty grouping */
+    assert_non_null(mod = lys_parse_mem(ctx, "module g {namespace urn:g;prefix g; grouping grp; uses grp;}", LYS_IN_YANG));
+    assert_null(mod->compiled->data);
+
     /* invalid */
     assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG));
     logbuf_assert("Grouping \"missinggrp\" referenced by a uses statement not found.");
@@ -2280,6 +2455,15 @@
                               "container top {uses grp {augment /g {leaf x {type int8;}}}}}", LYS_IN_YANG));
     logbuf_assert("Invalid descendant-schema-nodeid value \"/g\" - absolute-schema-nodeid used.");
 
+    assert_non_null(mod = lys_parse_mem(ctx, "module hh {yang-version 1.1;namespace urn:hh;prefix hh;"
+                                        "grouping grp {notification g { description \"super g\";}}"
+                                        "container top {notification h; uses grp {refine h {description \"ultra h\";}}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid descendant-schema-nodeid value \"h\" - target node not found.");
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module ii {yang-version 1.1;namespace urn:ii;prefix ii;"
+                                        "grouping grp {action g { description \"super g\";}}"
+                                        "container top {action i; uses grp {refine i {description \"ultra i\";}}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid descendant-schema-nodeid value \"i\" - target node not found.");
 
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);
@@ -3017,10 +3201,6 @@
                               "deviation /x {deviate replace {type empty;}}}", LYS_IN_YANG));
     logbuf_assert("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.");
 
-    assert_null(lys_parse_mem(ctx, "module oo {yang-version 1.1; namespace urn:oo;prefix oo; rpc test {input {container a {leaf b {type string;}}}}"
-                              "augment /test/input/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG));
-    logbuf_assert("Action \"invalid\" is placed inside another RPC/action.");
-
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);
 }
@@ -3050,6 +3230,8 @@
         cmocka_unit_test_setup_teardown(test_node_choice, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_node_anydata, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_action, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_notification, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_grouping, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_uses, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_refine, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_augment, logger_setup, logger_teardown),
diff --git a/tests/src/test_tree_schema_helpers.c b/tests/src/test_tree_schema_helpers.c
index c3b3e50..563a2f8 100644
--- a/tests/src/test_tree_schema_helpers.c
+++ b/tests/src/test_tree_schema_helpers.c
@@ -12,25 +12,17 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#include "../../src/common.c"
-#include "../../src/log.c"
-#include "../../src/set.c"
-#include "../../src/parser_yang.c"
-#include "../../src/tree_schema.c"
-#include "../../src/tree_schema_compile.c"
-#include "../../src/tree_schema_free.c"
-#include "../../src/tree_schema_helpers.c"
-#include "../../src/hash_table.c"
-#include "../../src/xpath.c"
-#include "../../src/context.c"
-
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <setjmp.h>
 #include <cmocka.h>
 
-#include "libyang.h"
+#include <string.h>
+
+#include "../../src/common.h"
+#include "../../src/context.h"
+#include "../../src/tree_schema_internal.h"
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
diff --git a/tests/src/test_xml.c b/tests/src/test_xml.c
index e3b3289..7b568d3 100644
--- a/tests/src/test_xml.c
+++ b/tests/src/test_xml.c
@@ -12,21 +12,22 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#include "common.h"
-#include "../../src/set.c"
-#include "../../src/xml.c"
-#include "../../src/common.c"
-#include "../../src/log.c"
+#define _DEFAULT_SOURCE
+#define _GNU_SOURCE
 
 #include <stdarg.h>
 #include <stddef.h>
 #include <setjmp.h>
 #include <cmocka.h>
 
+#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 
-#include "libyang.h"
+#include "../../src/xml.h"
+
+LY_ERR lyxml_ns_add(struct lyxml_context *context, const char *prefix, size_t prefix_len, char *uri);
+LY_ERR lyxml_ns_rm(struct lyxml_context *context);
 
 #define BUFSIZE 1024
 char logbuf[BUFSIZE] = {0};
@@ -94,7 +95,7 @@
     /* end element */
     str = "</element>";
     assert_int_equal(LY_EVALID, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
-    logbuf_assert("Opening and closing elements tag missmatch (\"element>\"). Line number 1.");
+    logbuf_assert("Opening and closing elements tag missmatch (\"element\"). Line number 1.");
 
 
     /* no element */
@@ -112,6 +113,13 @@
     assert_ptr_equal(p, str); /* input data not eaten */
     logbuf_assert("Document Type Declaration not supported. Line number 1.");
 
+    /* invalid XML */
+    str = p = "<!NONSENCE/>";
+    assert_int_equal(LY_EVALID, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
+    assert_null(name);
+    assert_ptr_equal(p, str); /* input data not eaten */
+    logbuf_assert("Unknown XML section \"<!NONSENCE/>\". Line number 1.");
+
     /* unqualified element */
     str = "  <  element/>";
     assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
@@ -176,7 +184,7 @@
     /* cleean context by getting closing tag */
     str += 12;
     assert_int_equal(LY_EVALID, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
-    logbuf_assert("Opening and closing elements tag missmatch (\"element>\"). Line number 1.");
+    logbuf_assert("Opening and closing elements tag missmatch (\"element\"). Line number 1.");
     str = "</yin:element/>";
     assert_int_equal(LY_EVALID, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
     logbuf_assert("Unexpected data \"/>\" in closing element tag. Line number 1.");
@@ -199,6 +207,16 @@
     str = "<yin:c⁐element>";
     assert_int_equal(LY_EVALID, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
     logbuf_assert("Invalid character sequence \"⁐element>\", expected whitespace or element tag termination ('>' or '/>'. Line number 1.");
+
+    /* mixed content */
+    str = "<a>text <b>x</b></a>";
+    assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
+    assert_string_equal("text <b>x</b></a>", str);
+    assert_int_equal(LYXML_ELEM_CONTENT, ctx.status);
+    assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &buf, &buf_len, &out, &len, &dynamic));
+    logbuf_assert("Mixed XML content is not allowed (text <b>). Line number 1.");
+    lyxml_context_clear(&ctx);
+
 }
 
 static void
@@ -239,26 +257,26 @@
     /* valid attribute */
     str = "xmlns=\"urn\">";
     assert_int_equal(LY_SUCCESS, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
-    assert_non_null(name);
+    assert_null(name);
     assert_null(prefix);
-    assert_int_equal(5, name_len);
+    assert_int_equal(0, name_len);
     assert_int_equal(0, prefix_len);
-    assert_false(strncmp("xmlns", name, name_len));
-    assert_string_equal("\"urn\">", str);
-    assert_int_equal(LYXML_ATTR_CONTENT, ctx.status);
+    assert_int_equal(1, ctx.ns.count);
+    assert_string_equal("", str);
+    assert_int_equal(LYXML_ELEM_CONTENT, ctx.status);
 
-    str = "xmlns:nc\n = \'urn\'/>";
+    str = "xmlns:nc\n = \'urn\'>";
     assert_int_equal(LY_SUCCESS, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
-    assert_non_null(name);
-    assert_non_null(prefix);
-    assert_int_equal(2, name_len);
-    assert_int_equal(5, prefix_len);
+    assert_null(name);
+    assert_null(prefix);
+    assert_int_equal(0, name_len);
+    assert_int_equal(0, prefix_len);
     assert_int_equal(3, ctx.line);
-    assert_false(strncmp("xmlns", prefix, prefix_len));
-    assert_false(strncmp("nc", name, name_len));
-    assert_string_equal("\'urn\'/>", str);
-    assert_int_equal(LYXML_ATTR_CONTENT, ctx.status);
+    assert_int_equal(2, ctx.ns.count);
+    assert_string_equal("", str);
+    assert_int_equal(LYXML_ELEM_CONTENT, ctx.status);
 
+    lyxml_context_clear(&ctx);
 }
 
 static void
@@ -270,6 +288,8 @@
     int dynamic;
     const char *str, *p;
     char *buf = NULL, *out = NULL;
+    const char *prefix, *name;
+    size_t prefix_len, name_len;
 
     struct lyxml_context ctx;
     memset(&ctx, 0, sizeof ctx);
@@ -298,28 +318,31 @@
 
     /* empty element content - only formating before defining child */
     ctx.status = LYXML_ELEM_CONTENT;
-    str = "\n  <";
+    str = "<x>\n  <y>";
+    assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
     assert_int_equal(LY_EINVAL, lyxml_get_string(&ctx, &str, &buf, &buf_len, &out, &len, &dynamic));
     assert_null(buf);
-    assert_string_equal("<", str);
+    assert_string_equal("<y>", str);
+    lyxml_context_clear(&ctx);
 
     /* empty element content is invalid - missing content terminating character < */
     ctx.status = LYXML_ELEM_CONTENT;
     str = "";
     assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &buf, &buf_len, &out, &len, &dynamic));
     assert_null(buf);
-    logbuf_assert("Unexpected end-of-file. Line number 2.");
+    logbuf_assert("Unexpected end-of-input. Line number 2.");
 
     ctx.status = LYXML_ELEM_CONTENT;
     str = p = "xxx";
     assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &buf, &buf_len, &out, &len, &dynamic));
     assert_null(buf);
-    logbuf_assert("Unexpected end-of-file. Line number 2.");
+    logbuf_assert("Unexpected end-of-input. Line number 2.");
     assert_ptr_equal(p, str); /* input data not eaten */
 
     /* valid strings */
     ctx.status = LYXML_ELEM_CONTENT;
-    str = "€𠜎Øn \n&lt;&amp;&quot;&apos;&gt; &#82;&#x4f;&#x4B;<";
+    str = "<a>€𠜎Øn \n&lt;&amp;&quot;&apos;&gt; &#82;&#x4f;&#x4B;</a>";
+    assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
     assert_int_equal(LY_SUCCESS, lyxml_get_string(&ctx, &str, &buf, &buf_len, &out, &len, &dynamic));
     assert_int_not_equal(0, dynamic);
     assert_non_null(buf);
@@ -327,8 +350,9 @@
     assert_int_equal(22, buf_len);
     assert_int_equal(21, len);
     assert_string_equal("€𠜎Øn \n<&\"\'> ROK", buf);
-    assert_string_equal("<", str);
+    assert_string_equal("", str);
     assert_int_equal(LYXML_ELEMENT, ctx.status);
+    lyxml_context_clear(&ctx);
 
     /* test using n-bytes UTF8 hexadecimal code points */
     ctx.status = LYXML_ATTR_CONTENT;
@@ -396,20 +420,23 @@
 {
     (void) state; /* unused */
 
-    const char *e1, *e2;
     const struct lyxml_ns *ns;
 
     struct lyxml_context ctx;
     memset(&ctx, 0, sizeof ctx);
     ctx.line = 1;
 
-    e1 = "element1";
-    e2 = "element2";
-    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e1, NULL, 0, "urn:default", strlen("urn:default")));
-    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e1, "nc", 2, "urn:nc1", strlen("urn:nc1")));
-    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e2, "nc", 2, "urn:nc2", strlen("urn:nc2")));
-    assert_int_equal(3, (&ctx)->ns.count);
-    assert_int_not_equal(0, (&ctx)->ns.size);
+    /* simulate adding open element1 into context */
+    ctx.elements.count++;
+    /* processing namespace definitions */
+    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, NULL, 0, strdup("urn:default")));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, "nc", 2, strdup("urn:nc1")));
+    /* simulate adding open element2 into context */
+    ctx.elements.count++;
+    /* processing namespace definitions */
+    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, "nc", 2, strdup("urn:nc2")));
+    assert_int_equal(3, ctx.ns.count);
+    assert_int_not_equal(0, ctx.ns.size);
 
     ns = lyxml_ns_get(&ctx, NULL, 0);
     assert_non_null(ns);
@@ -421,16 +448,20 @@
     assert_string_equal("nc", ns->prefix);
     assert_string_equal("urn:nc2", ns->uri);
 
-    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx, e2));
-    assert_int_equal(2, (&ctx)->ns.count);
+    /* simulate closing element2 */
+    ctx.elements.count--;
+    lyxml_ns_rm(&ctx);
+    assert_int_equal(2, ctx.ns.count);
 
     ns = lyxml_ns_get(&ctx, "nc", 2);
     assert_non_null(ns);
     assert_string_equal("nc", ns->prefix);
     assert_string_equal("urn:nc1", ns->uri);
 
-    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx, e1));
-    assert_int_equal(0, (&ctx)->ns.count);
+    /* simulate closing element1 */
+    ctx.elements.count--;
+    lyxml_ns_rm(&ctx);
+    assert_int_equal(0, ctx.ns.count);
 
     assert_null(lyxml_ns_get(&ctx, "nc", 2));
     assert_null(lyxml_ns_get(&ctx, NULL, 0));
@@ -470,6 +501,33 @@
     free(to_free);
 }
 
+static void
+test_ns2(void **state)
+{
+    (void) state; /* unused */
+
+    struct lyxml_context ctx;
+    memset(&ctx, 0, sizeof ctx);
+    ctx.line = 1;
+
+    /* simulate adding open element1 into context */
+    ctx.elements.count++;
+    /* default namespace defined in parent element1 */
+    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, NULL, 0, strdup("urn:default")));
+    assert_int_equal(1, ctx.ns.count);
+    /* going into child element1 */
+    /* simulate adding open element1 into context */
+    ctx.elements.count++;
+    /* no namespace defined, going out (first, simulate closing of so far open element) */
+    ctx.elements.count--;
+    lyxml_ns_rm(&ctx);
+    assert_int_equal(1, ctx.ns.count);
+    /* nothing else, going out of the parent element1 (first, simulate closing of so far open element) */
+    ctx.elements.count--;
+    lyxml_ns_rm(&ctx);
+    assert_int_equal(0, ctx.ns.count);
+}
+
 int main(void)
 {
     const struct CMUnitTest tests[] = {
@@ -478,6 +536,7 @@
         cmocka_unit_test_setup(test_text, logger_setup),
         cmocka_unit_test_setup(test_ns, logger_setup),
         cmocka_unit_test(test_simple_xml),
+        cmocka_unit_test_setup(test_ns2, logger_setup),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tools/config.h.in b/tools/config.h.in
new file mode 100644
index 0000000..f65d92d
--- /dev/null
+++ b/tools/config.h.in
@@ -0,0 +1,23 @@
+/**
+ * @file config.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief various variables detected by cmake
+ *
+ * Copyright (c) 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
+ */
+
+#ifndef YANGLINT_CONFIG_H_
+#define YANGLINT_CONFIG_H_
+
+#define _DEFAULT_SOURCE
+#define _GNU_SOURCE
+
+#define PROJECT_VERSION "@LIBYANG_VERSION@" /**< libyang project version string */
+
+#endif /* YANGLINT_CONFIG_H_ */
diff --git a/tools/lint/commands.c b/tools/lint/commands.c
new file mode 100644
index 0000000..4b65a76
--- /dev/null
+++ b/tools/lint/commands.c
@@ -0,0 +1,1569 @@
+/**
+ * @file commands.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang's yanglint tool commands
+ *
+ * Copyright (c) 2015 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 "config.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include "commands.h"
+#include "libyang.h"
+
+COMMAND commands[];
+extern int done;
+extern struct ly_ctx *ctx;
+
+void
+cmd_add_help(void)
+{
+    printf("add [-i] <path-to-model> [<paths-to-other-models> ...]\n");
+    printf("\t-i         - make all the imported modules implemented\n");
+}
+
+void
+cmd_load_help(void)
+{
+    printf("load [-i] <model-name> [<other-model-names> ...]\n");
+    printf("\t-i         - make all the imported modules implemented\n");
+}
+
+void
+cmd_clear_help(void)
+{
+    printf("clear [<yang-library> | -e]\n");
+    printf("\t Replace the current context with an empty one, searchpaths are not kept.\n");
+    printf("\t If <yang-library> path specified, load the modules according to the yang library data.\n");
+    printf("\t Option '-e' causes ietf-yang-library will not be loaded.\n");
+}
+
+void
+cmd_print_help(void)
+{
+    printf("print [-f (yang | yin | tree [<tree-options>] | info [-P <info-path>] | jsons)] [-o <output-file>]"
+           " <model-name>[@<revision>]\n");
+    printf("\n");
+    printf("\ttree-options:\t--tree-print-groupings\t(print top-level groupings in a separate section)\n");
+    printf("\t             \t--tree-print-uses\t(print uses nodes instead the resolved grouping nodes)\n");
+    printf("\t             \t--tree-no-leafref-target\t(do not print the target nodes of leafrefs)\n");
+    printf("\t             \t--tree-path <schema-path>\t(print only the specified subtree)\n");
+    printf("\t             \t--tree-line-length <line-length>\t(wrap lines if longer than line-length,\n");
+    printf("\t             \t\tnot a strict limit, longer lines can often appear)\n");
+    printf("\n");
+    printf("\tinfo-path:\t<schema-path> | typedef[<schema-path>]/<typedef-name> |\n");
+    printf("\t          \t| identity/<identity-name> | feature/<feature-name> |\n");
+    printf("\t          \t| grouping[<schema-path>]/<grouping-name> |\n");
+    printf("\t          \t| type/<schema-path-leaf-or-leaflist>\n");
+    printf("\n");
+    printf("\tschema-path:\t( /<module-name>:<node-identifier> )+\n");
+}
+
+void
+cmd_data_help(void)
+{
+    printf("data [-(-s)trict] [-t TYPE] [-d DEFAULTS] [-o <output-file>] [-f (xml | json | lyb)] [-r <running-file-name>]\n");
+    printf("     <data-file-name> [<RPC/action-data-file-name> | <yang-data name>]\n\n");
+    printf("Accepted TYPEs:\n");
+    printf("\tauto       - resolve data type (one of the following) automatically (as pyang does),\n");
+    printf("\t             this option is applicable only in case of XML input data.\n");
+    printf("\tdata       - LYD_OPT_DATA (default value) - complete datastore including status data.\n");
+    printf("\tconfig     - LYD_OPT_CONFIG - complete configuration datastore.\n");
+    printf("\tget        - LYD_OPT_GET - <get> operation result.\n");
+    printf("\tgetconfig  - LYD_OPT_GETCONFIG - <get-config> operation result.\n");
+    printf("\tedit       - LYD_OPT_EDIT - <edit-config>'s data (content of its <config> element).\n");
+    printf("\trpc        - LYD_OPT_RPC - NETCONF RPC message.\n");
+    printf("\trpcreply   - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
+    printf("\tnotif      - LYD_OPT_NOTIF - NETCONF Notification message.\n");
+    printf("\tyangdata   - LYD_OPT_DATA_TEMPLATE - yang-data extension (last parameter mandatory in this case)\n\n");
+    printf("Accepted DEFAULTS:\n");
+    printf("\tall        - add missing default nodes\n");
+    printf("\tall-tagged - add missing default nodes and mark all the default nodes with the attribute.\n");
+    printf("\ttrim       - remove all nodes with a default value\n");
+    printf("\timplicit-tagged    - add missing nodes and mark them with the attribute\n\n");
+    printf("Option -r:\n");
+    printf("\tOptional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the file contains running\n");
+    printf("\tconfiguration datastore data referenced from the RPC/Notification. Note that the file is\n");
+    printf("\tvalidated as 'data' TYPE. Special value '!' can be used as argument to ignore the\n");
+    printf("\texternal references.\n\n");
+    printf("\tIf an XPath expression (when/must) needs access to configuration data, you can provide\n");
+    printf("\tthem in a file, which will be parsed as 'data' TYPE.\n\n");
+}
+
+void
+cmd_xpath_help(void)
+{
+    printf("xpath [-t TYPE] [-x <additional-tree-file-name>] -e <XPath-expression>\n"
+           "      <XML-data-file-name> [<JSON-rpc/action-schema-nodeid>]\n");
+    printf("Accepted TYPEs:\n");
+    printf("\tauto       - resolve data type (one of the following) automatically (as pyang does),\n");
+    printf("\t             this option is applicable only in case of XML input data.\n");
+    printf("\tconfig     - LYD_OPT_CONFIG\n");
+    printf("\tget        - LYD_OPT_GET\n");
+    printf("\tgetconfig  - LYD_OPT_GETCONFIG\n");
+    printf("\tedit       - LYD_OPT_EDIT\n");
+    printf("\trpc        - LYD_OPT_RPC\n");
+    printf("\trpcreply   - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
+    printf("\tnotif      - LYD_OPT_NOTIF\n\n");
+    printf("Option -x:\n");
+    printf("\tIf RPC/action/notification/RPC reply (for TYPEs 'rpc', 'rpcreply', and 'notif') includes\n");
+    printf("\tan XPath expression (when/must) that needs access to the configuration data, you can provide\n");
+    printf("\tthem in a file, which will be parsed as 'config'.\n");
+}
+
+void
+cmd_list_help(void)
+{
+    printf("list [-f (xml | json)]\n\n");
+    printf("\tBasic list output (no -f): i - imported module, I - implemented module\n");
+}
+
+void
+cmd_feature_help(void)
+{
+    printf("feature [ -(-e)nable | -(-d)isable (* | <feature-name>[,<feature-name> ...]) ] <model-name>[@<revision>]\n");
+}
+
+void
+cmd_searchpath_help(void)
+{
+    printf("searchpath [<model-dir-path> | --clear]\n\n");
+    printf("\tThey are used to search for imports and includes of a model.\n");
+    printf("\tThe \"load\" command uses these directories to find models directly.\n");
+}
+
+void
+cmd_verb_help(void)
+{
+    printf("verb (error/0 | warning/1 | verbose/2 | debug/3)\n");
+}
+
+#ifndef NDEBUG
+
+void
+cmd_debug_help(void)
+{
+    printf("debug (dict | yang | yin | xpath | diff)+\n");
+}
+
+#endif
+
+LYS_INFORMAT
+get_schema_format(const char *path)
+{
+    char *ptr;
+
+    if ((ptr = strrchr(path, '.')) != NULL) {
+        ++ptr;
+        if (!strcmp(ptr, "yang")) {
+            return LYS_IN_YANG;
+        /* TODO YIN parser not yet implemented
+        } else if (!strcmp(ptr, "yin")) {
+             return LYS_IN_YIN;
+        */
+        } else {
+            fprintf(stderr, "Input file in an unknown format \"%s\".\n", ptr);
+            return LYS_IN_UNKNOWN;
+        }
+    } else {
+        fprintf(stdout, "Input file \"%s\" without extension - unknown format.\n", path);
+        return LYS_IN_UNKNOWN;
+    }
+}
+
+int
+cmd_add(const char *arg)
+{
+    int path_len, ret = 1, index = 0;
+    char *path, *dir, *s, *arg_ptr;
+    const char * const *searchpaths;
+    const struct lys_module *model;
+    LYS_INFORMAT format = LYS_IN_UNKNOWN;
+
+    if (strlen(arg) < 5) {
+        cmd_add_help();
+        return 1;
+    }
+
+    arg_ptr = strdup(arg + 3 /* ignore "add" */);
+
+    for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
+        if (s[2] == '\0' || s[2] == ' ') {
+            ly_ctx_set_option(ctx, LY_CTX_ALLIMPLEMENTED);
+            s[0] = s[1] = ' ';
+        }
+    }
+    s = arg_ptr;
+
+    while (arg_ptr[0] == ' ') {
+        ++arg_ptr;
+    }
+    if (strchr(arg_ptr, ' ')) {
+        path_len = strchr(arg_ptr, ' ') - arg_ptr;
+    } else {
+        path_len = strlen(arg_ptr);
+    }
+    path = strndup(arg_ptr, path_len);
+
+    searchpaths = ly_ctx_get_searchdirs(ctx);
+    if (searchpaths) {
+        for (index = 0; searchpaths[index]; index++);
+    }
+
+    while (path) {
+        format = get_schema_format(path);
+        if (format == LYS_IN_UNKNOWN) {
+            free(path);
+            goto cleanup;
+        }
+
+        dir = strdup(path);
+        ly_ctx_set_searchdir(ctx, dirname(dir));
+        model = lys_parse_path(ctx, path, format);
+        ly_ctx_unset_searchdir(ctx, index);
+        free(path);
+        free(dir);
+
+        if (!model) {
+            /* libyang printed the error messages */
+            goto cleanup;
+        }
+
+        /* next model */
+        arg_ptr += path_len;
+        while (arg_ptr[0] == ' ') {
+            ++arg_ptr;
+        }
+        if (strchr(arg_ptr, ' ')) {
+            path_len = strchr(arg_ptr, ' ') - arg_ptr;
+        } else {
+            path_len = strlen(arg_ptr);
+        }
+
+        if (path_len) {
+            path = strndup(arg_ptr, path_len);
+        } else {
+            path = NULL;
+        }
+    }
+    if (format == LYS_IN_UNKNOWN) {
+        /* no schema on input */
+        cmd_add_help();
+        goto cleanup;
+    }
+    ret = 0;
+
+cleanup:
+    free(s);
+    ly_ctx_unset_option(ctx, LY_CTX_ALLIMPLEMENTED);
+
+    return ret;
+}
+
+int
+cmd_load(const char *arg)
+{
+    int name_len, ret = 1;
+    char *name, *s, *arg_ptr;
+    const struct lys_module *model;
+
+    if (strlen(arg) < 6) {
+        cmd_load_help();
+        return 1;
+    }
+
+    arg_ptr = strdup(arg + 4 /* ignore "load" */);
+
+    for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
+        if (s[2] == '\0' || s[2] == ' ') {
+            ly_ctx_set_option(ctx, LY_CTX_ALLIMPLEMENTED);
+            s[0] = s[1] = ' ';
+        }
+    }
+    s = arg_ptr;
+
+    while (arg_ptr[0] == ' ') {
+        ++arg_ptr;
+    }
+    if (strchr(arg_ptr, ' ')) {
+        name_len = strchr(arg_ptr, ' ') - arg_ptr;
+    } else {
+        name_len = strlen(arg_ptr);
+    }
+    name = strndup(arg_ptr, name_len);
+
+    while (name) {
+        model = ly_ctx_load_module(ctx, name, NULL);
+        free(name);
+        if (!model) {
+            /* libyang printed the error messages */
+            goto cleanup;
+        }
+
+        /* next model */
+        arg_ptr += name_len;
+        while (arg_ptr[0] == ' ') {
+            ++arg_ptr;
+        }
+        if (strchr(arg_ptr, ' ')) {
+            name_len = strchr(arg_ptr, ' ') - arg_ptr;
+        } else {
+            name_len = strlen(arg_ptr);
+        }
+
+        if (name_len) {
+            name = strndup(arg_ptr, name_len);
+        } else {
+            name = NULL;
+        }
+    }
+    ret = 0;
+
+cleanup:
+    free(s);
+    ly_ctx_unset_option(ctx, LY_CTX_ALLIMPLEMENTED);
+
+    return ret;
+}
+
+int
+cmd_print(const char *arg)
+{
+    int c, argc, option_index, ret = 1, tree_ll = 0, tree_opts = 0, compiled = 0;
+    char **argv = NULL, *ptr, *model_name, *revision;
+    const char *out_path = NULL;
+    const struct lys_module *module;
+    LYS_OUTFORMAT format = LYS_OUT_TREE;
+    FILE *output = stdout;
+    static struct option long_options[] = {
+        {"compiled", no_argument, 0, 'c'},
+        {"help", no_argument, 0, 'h'},
+        {"format", required_argument, 0, 'f'},
+        {"output", required_argument, 0, 'o'},
+#if 0
+        {"tree-print-groupings", no_argument, 0, 'g'},
+        {"tree-print-uses", no_argument, 0, 'u'},
+        {"tree-no-leafref-target", no_argument, 0, 'n'},
+        {"tree-path", required_argument, 0, 'P'},
+        {"info-path", required_argument, 0, 'P'},
+        {"tree-line-length", required_argument, 0, 'L'},
+#endif
+        {NULL, 0, 0, 0}
+    };
+    void *rlcd;
+
+    argc = 1;
+    argv = malloc(2*sizeof *argv);
+    *argv = strdup(arg);
+    ptr = strtok(*argv, " ");
+    while ((ptr = strtok(NULL, " "))) {
+        rlcd = realloc(argv, (argc+2)*sizeof *argv);
+        if (!rlcd) {
+            fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+            goto cleanup;
+        }
+        argv = rlcd;
+        argv[argc++] = ptr;
+    }
+    argv[argc] = NULL;
+
+    optind = 0;
+    while (1) {
+        option_index = 0;
+        c = getopt_long(argc, argv, "chf:go:guP:L:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'c':
+            compiled = 1;
+            break;
+        case 'h':
+            cmd_print_help();
+            ret = 0;
+            goto cleanup;
+        case 'f':
+            if (!strcmp(optarg, "yang")) {
+                format = LYS_OUT_YANG;
+#if 0
+            } else if (!strcmp(optarg, "yin")) {
+                format = LYS_OUT_YIN;
+            } else if (!strcmp(optarg, "tree")) {
+                format = LYS_OUT_TREE;
+            } else if (!strcmp(optarg, "tree-rfc")) {
+                format = LYS_OUT_TREE;
+                tree_opts |= LYS_OUTOPT_TREE_RFC;
+            } else if (!strcmp(optarg, "info")) {
+                format = LYS_OUT_INFO;
+            } else if (!strcmp(optarg, "jsons")) {
+                format = LYS_OUT_JSON;
+#endif
+            } else {
+                fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
+                goto cleanup;
+            }
+            break;
+        case 'o':
+            if (out_path) {
+                fprintf(stderr, "Output specified twice.\n");
+                goto cleanup;
+            }
+            out_path = optarg;
+            break;
+#if 0
+        case 'g':
+            tree_opts |= LYS_OUTOPT_TREE_GROUPING;
+            break;
+        case 'u':
+            tree_opts |= LYS_OUTOPT_TREE_USES;
+            break;
+        case 'n':
+            tree_opts |= LYS_OUTOPT_TREE_NO_LEAFREF;
+            break;
+        case 'P':
+            target_path = optarg;
+            break;
+        case 'L':
+            tree_ll = atoi(optarg);
+            break;
+#endif
+        case '?':
+            fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
+            goto cleanup;
+        }
+    }
+
+    /* file name */
+    if (optind == argc) {
+        fprintf(stderr, "Missing the module name.\n");
+        goto cleanup;
+    }
+
+    /* compiled format */
+    if (compiled) {
+        format++;
+    }
+#if 0
+    /* tree fromat with or without gropings */
+    if ((tree_opts || tree_ll) && format != LYS_OUT_TREE) {
+        fprintf(stderr, "--tree options take effect only in case of the tree output format.\n");
+    }
+#endif
+    /* module, revision */
+    model_name = argv[optind];
+    revision = NULL;
+    if (strchr(model_name, '@')) {
+        revision = strchr(model_name, '@');
+        revision[0] = '\0';
+        ++revision;
+    }
+
+    if (revision) {
+        module = ly_ctx_get_module(ctx, model_name, revision);
+    } else {
+        module = ly_ctx_get_module_latest(ctx, model_name);
+    }
+#if 0
+    if (!module) {
+        /* not a module, try to find it as a submodule */
+        module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
+    }
+#endif
+
+    if (!module) {
+        if (revision) {
+            fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
+        } else {
+            fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
+        }
+        goto cleanup;
+    }
+
+    if (out_path) {
+        output = fopen(out_path, "w");
+        if (!output) {
+            fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
+            goto cleanup;
+        }
+    }
+
+    ret = lys_print_file(output, module, format, tree_ll, tree_opts);
+    if (format == LYS_OUT_JSON) {
+        fputs("\n", output);
+    }
+
+cleanup:
+    free(*argv);
+    free(argv);
+
+    if (output && (output != stdout)) {
+        fclose(output);
+    }
+
+    return ret;
+}
+
+static LYD_FORMAT
+detect_data_format(char *filepath)
+{
+    size_t len;
+
+    /* detect input format according to file suffix */
+    len = strlen(filepath);
+    for (; isspace(filepath[len - 1]); len--, filepath[len] = '\0'); /* remove trailing whitespaces */
+    if (len >= 5 && !strcmp(&filepath[len - 4], ".xml")) {
+        return LYD_XML;
+#if 0
+    } else if (len >= 6 && !strcmp(&filepath[len - 5], ".json")) {
+        return LYD_JSON;
+    } else if (len >= 5 && !strcmp(&filepath[len - 4], ".lyb")) {
+        return LYD_LYB;
+#endif
+    } else {
+        return LYD_UNKNOWN;
+    }
+}
+
+static int
+parse_data(char *filepath, int *options, struct lyd_node *val_tree, const char *rpc_act_file,
+           struct lyd_node **result)
+{
+    LYD_FORMAT informat = LYD_UNKNOWN;
+    struct lyd_node *data = NULL, *rpc_act = NULL;
+    int opts = *options;
+
+    /* detect input format according to file suffix */
+    informat = detect_data_format(filepath);
+    if (informat == LYD_UNKNOWN) {
+        fprintf(stderr, "Unable to resolve format of the input file, please add \".xml\", \".json\", or \".lyb\" suffix.\n");
+        return EXIT_FAILURE;
+    }
+
+    ly_err_clean(ctx, NULL);
+
+#if 0
+    if ((opts & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
+        /* automatically detect data type from the data top level */
+        if (informat != LYD_XML) {
+            fprintf(stderr, "Only XML data can be automatically explored.\n");
+            return EXIT_FAILURE;
+        }
+
+        xml = lyxml_parse_path(ctx, filepath, 0);
+        if (!xml) {
+            fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
+            return EXIT_FAILURE;
+        }
+
+        /* NOTE: namespace is ignored to simplify usage of this feature */
+
+        if (!strcmp(xml->name, "data")) {
+            fprintf(stdout, "Parsing %s as complete datastore.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_ADD_YANGLIB;
+        } else if (!strcmp(xml->name, "config")) {
+            fprintf(stdout, "Parsing %s as config data.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
+        } else if (!strcmp(xml->name, "get-reply")) {
+            fprintf(stdout, "Parsing %s as <get> reply data.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
+        } else if (!strcmp(xml->name, "get-config-reply")) {
+            fprintf(stdout, "Parsing %s as <get-config> reply data.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
+        } else if (!strcmp(xml->name, "edit-config")) {
+            fprintf(stdout, "Parsing %s as <edit-config> data.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
+        } else if (!strcmp(xml->name, "rpc")) {
+            fprintf(stdout, "Parsing %s as <rpc> data.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
+        } else if (!strcmp(xml->name, "rpc-reply")) {
+            if (!rpc_act_file) {
+                fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
+                lyxml_free(ctx, xml);
+                return EXIT_FAILURE;
+            }
+            fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
+            rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
+            if (!rpc_act) {
+                fprintf(stderr, "Failed to parse RPC/action.\n");
+                lyxml_free(ctx, xml);
+                return EXIT_FAILURE;
+            }
+        } else if (!strcmp(xml->name, "notification")) {
+            fprintf(stdout, "Parsing %s as <notification> data.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
+        } else if (!strcmp(xml->name, "yang-data")) {
+            fprintf(stdout, "Parsing %s as <yang-data> data.\n", filepath);
+            opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
+            if (!rpc_act_file) {
+                fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
+                lyxml_free(ctx, xml);
+                return EXIT_FAILURE;
+            }
+        } else {
+            fprintf(stderr, "Invalid top-level element for automatic data type recognition.\n");
+            lyxml_free(ctx, xml);
+            return EXIT_FAILURE;
+        }
+
+        if (opts & LYD_OPT_RPCREPLY) {
+            data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act, val_tree);
+        } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
+            data = lyd_parse_xml(ctx, &xml->child, opts, val_tree);
+        } else if (opts & LYD_OPT_DATA_TEMPLATE) {
+            data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act_file);
+        } else {
+            data = lyd_parse_xml(ctx, &xml->child, opts);
+        }
+        lyxml_free(ctx, xml);
+    } else {
+#endif
+        if (opts & LYD_OPT_RPCREPLY) {
+            if (!rpc_act_file) {
+                fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
+                return EXIT_FAILURE;
+            }
+            rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
+            if (!rpc_act) {
+                fprintf(stderr, "Failed to parse RPC/action.\n");
+                return EXIT_FAILURE;
+            }
+            data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act, val_tree);
+        } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
+            data = lyd_parse_path(ctx, filepath, informat, opts, val_tree);
+        } else if (opts & LYD_OPT_DATA_TEMPLATE) {
+            if (!rpc_act_file) {
+                fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
+                return EXIT_FAILURE;
+            }
+            data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act_file);
+        } else {
+            if (!(opts & LYD_OPT_TYPEMASK)) {
+                /* automatically add yang-library data */
+                opts |= LYD_OPT_DATA_ADD_YANGLIB;
+            }
+            data = lyd_parse_path(ctx, filepath, informat, opts);
+        }
+#if 0
+    }
+#endif
+    lyd_free_all(rpc_act);
+
+    if (ly_err_first(ctx)) {
+        fprintf(stderr, "Failed to parse data.\n");
+        lyd_free_all(data);
+        return EXIT_FAILURE;
+    }
+
+    *result = data;
+    *options = opts;
+    return EXIT_SUCCESS;
+}
+
+int
+cmd_data(const char *arg)
+{
+    int c, argc, option_index, ret = 1;
+    int options = 0, printopt = 0;
+    char **argv = NULL, *ptr;
+    const char *out_path = NULL;
+    struct lyd_node *data = NULL, *val_tree = NULL;
+    LYD_FORMAT outformat = LYD_UNKNOWN;
+    FILE *output = stdout;
+    static struct option long_options[] = {
+        {"defaults", required_argument, 0, 'd'},
+        {"help", no_argument, 0, 'h'},
+        {"format", required_argument, 0, 'f'},
+        {"option", required_argument, 0, 't'},
+        {"output", required_argument, 0, 'o'},
+        {"running", required_argument, 0, 'r'},
+        {"strict", no_argument, 0, 's'},
+        {NULL, 0, 0, 0}
+    };
+    void *rlcd;
+
+    argc = 1;
+    argv = malloc(2*sizeof *argv);
+    *argv = strdup(arg);
+    ptr = strtok(*argv, " ");
+    while ((ptr = strtok(NULL, " "))) {
+        rlcd = realloc(argv, (argc + 2) * sizeof *argv);
+        if (!rlcd) {
+            fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+            goto cleanup;
+        }
+        argv = rlcd;
+        argv[argc++] = ptr;
+    }
+    argv[argc] = NULL;
+
+    optind = 0;
+    while (1) {
+        option_index = 0;
+        c = getopt_long(argc, argv, "d:hf:o:st:r:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+#if 0
+        case 'd':
+            if (!strcmp(optarg, "all")) {
+                printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL;
+            } else if (!strcmp(optarg, "all-tagged")) {
+                printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
+            } else if (!strcmp(optarg, "trim")) {
+                printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_TRIM;
+            } else if (!strcmp(optarg, "implicit-tagged")) {
+                printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
+            }
+            break;
+#endif
+        case 'h':
+            cmd_data_help();
+            ret = 0;
+            goto cleanup;
+        case 'f':
+            if (!strcmp(optarg, "xml")) {
+                outformat = LYD_XML;
+#if 0
+            } else if (!strcmp(optarg, "json")) {
+                outformat = LYD_JSON;
+            } else if (!strcmp(optarg, "lyb")) {
+                outformat = LYD_LYB;
+#endif
+            } else {
+                fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
+                goto cleanup;
+            }
+            break;
+        case 'o':
+            if (out_path) {
+                fprintf(stderr, "Output specified twice.\n");
+                goto cleanup;
+            }
+            out_path = optarg;
+            break;
+        case 'r':
+            if (val_tree || (options & LYD_OPT_NOEXTDEPS)) {
+                fprintf(stderr, "The running datastore (-r) cannot be set multiple times.\n");
+                goto cleanup;
+            }
+            if (optarg[0] == '!') {
+                /* ignore extenral dependencies to the running datastore */
+                options |= LYD_OPT_NOEXTDEPS;
+            } else {
+                /* external file with the running datastore */
+                val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_DATA_NO_YANGLIB);
+                if (!val_tree) {
+                    fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
+                    goto cleanup;
+                }
+            }
+            break;
+        case 's':
+            options |= LYD_OPT_STRICT;
+            options |= LYD_OPT_OBSOLETE;
+            break;
+        case 't':
+            if (!strcmp(optarg, "auto")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
+            } else if (!strcmp(optarg, "data")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA;
+            } else if (!strcmp(optarg, "config")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
+            } else if (!strcmp(optarg, "get")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
+            } else if (!strcmp(optarg, "getconfig")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
+            } else if (!strcmp(optarg, "edit")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
+            } else if (!strcmp(optarg, "rpc")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
+            } else if (!strcmp(optarg, "rpcreply")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
+            } else if (!strcmp(optarg, "notif")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
+            } else if (!strcmp(optarg, "yangdata")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
+            } else {
+                fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
+                cmd_data_help();
+                goto cleanup;
+            }
+            break;
+        case '?':
+            fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
+            goto cleanup;
+        }
+    }
+
+    /* file name */
+    if (optind == argc) {
+        fprintf(stderr, "Missing the data file name.\n");
+        goto cleanup;
+    }
+
+    if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
+        goto cleanup;
+    }
+
+    if (out_path) {
+        output = fopen(out_path, "w");
+        if (!output) {
+            fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
+            goto cleanup;
+        }
+    }
+
+    if (outformat != LYD_UNKNOWN) {
+        if (options & LYD_OPT_RPCREPLY) {
+            lyd_print_file(output, lyd_node_children(data), outformat, LYDP_WITHSIBLINGS | LYDP_FORMAT | printopt);
+        } else {
+            lyd_print_file(output, data, outformat, LYDP_WITHSIBLINGS | LYDP_FORMAT | printopt);
+        }
+    }
+
+    ret = 0;
+
+cleanup:
+    free(*argv);
+    free(argv);
+
+    if (output && (output != stdout)) {
+        fclose(output);
+    }
+
+    lyd_free_all(val_tree);
+    lyd_free_all(data);
+
+    return ret;
+}
+#if 0
+int
+cmd_xpath(const char *arg)
+{
+    int c, argc, option_index, ret = 1, long_str;
+    char **argv = NULL, *ptr, *expr = NULL;
+    unsigned int i, j;
+    int options = 0;
+    struct lyd_node *data = NULL, *node, *val_tree = NULL;
+    struct lyd_node_leaf_list *key;
+    struct ly_set *set;
+    static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"expr", required_argument, 0, 'e'},
+        {NULL, 0, 0, 0}
+    };
+    void *rlcd;
+
+    long_str = 0;
+    argc = 1;
+    argv = malloc(2 * sizeof *argv);
+    *argv = strdup(arg);
+    ptr = strtok(*argv, " ");
+    while ((ptr = strtok(NULL, " "))) {
+        if (long_str) {
+            ptr[-1] = ' ';
+            if (ptr[strlen(ptr) - 1] == long_str) {
+                long_str = 0;
+                ptr[strlen(ptr) - 1] = '\0';
+            }
+        } else {
+            rlcd = realloc(argv, (argc + 2) * sizeof *argv);
+            if (!rlcd) {
+                fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+                goto cleanup;
+            }
+            argv = rlcd;
+            argv[argc] = ptr;
+            if (ptr[0] == '"') {
+                long_str = '"';
+                ++argv[argc];
+            }
+            if (ptr[0] == '\'') {
+                long_str = '\'';
+                ++argv[argc];
+            }
+            if (ptr[strlen(ptr) - 1] == long_str) {
+                long_str = 0;
+                ptr[strlen(ptr) - 1] = '\0';
+            }
+            ++argc;
+        }
+    }
+    argv[argc] = NULL;
+
+    optind = 0;
+    while (1) {
+        option_index = 0;
+        c = getopt_long(argc, argv, "he:t:x:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'h':
+            cmd_xpath_help();
+            ret = 0;
+            goto cleanup;
+        case 'e':
+            expr = optarg;
+            break;
+        case 't':
+            if (!strcmp(optarg, "auto")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
+            } else if (!strcmp(optarg, "config")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
+            } else if (!strcmp(optarg, "get")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
+            } else if (!strcmp(optarg, "getconfig")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
+            } else if (!strcmp(optarg, "edit")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
+            } else if (!strcmp(optarg, "rpc")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
+            } else if (!strcmp(optarg, "rpcreply")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
+            } else if (!strcmp(optarg, "notif")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
+            } else if (!strcmp(optarg, "yangdata")) {
+                options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
+            } else {
+                fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
+                cmd_data_help();
+                goto cleanup;
+            }
+            break;
+        case 'x':
+            val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
+            if (!val_tree) {
+                fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
+                goto cleanup;
+            }
+            break;
+        case '?':
+            fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
+            goto cleanup;
+        }
+    }
+
+    if (optind == argc) {
+        fprintf(stderr, "Missing the file with data.\n");
+        goto cleanup;
+    }
+
+    if (!expr) {
+        fprintf(stderr, "Missing the XPath expression.\n");
+        goto cleanup;
+    }
+
+    if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
+        goto cleanup;
+    }
+
+    if (!(set = lyd_find_path(data, expr))) {
+        goto cleanup;
+    }
+
+    /* print result */
+    printf("Result:\n");
+    if (!set->number) {
+        printf("\tEmpty\n");
+    } else {
+        for (i = 0; i < set->number; ++i) {
+            node = set->set.d[i];
+            switch (node->schema->nodetype) {
+            case LYS_CONTAINER:
+                printf("\tContainer ");
+                break;
+            case LYS_LEAF:
+                printf("\tLeaf ");
+                break;
+            case LYS_LEAFLIST:
+                printf("\tLeaflist ");
+                break;
+            case LYS_LIST:
+                printf("\tList ");
+                break;
+            case LYS_ANYXML:
+                printf("\tAnyxml ");
+                break;
+            case LYS_ANYDATA:
+                printf("\tAnydata ");
+                break;
+            default:
+                printf("\tUnknown ");
+                break;
+            }
+            printf("\"%s\"", node->schema->name);
+            if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+                printf(" (val: %s)", ((struct lyd_node_leaf_list *)node)->value_str);
+            } else if (node->schema->nodetype == LYS_LIST) {
+                key = (struct lyd_node_leaf_list *)node->child;
+                printf(" (");
+                for (j = 0; j < ((struct lys_node_list *)node->schema)->keys_size; ++j) {
+                    if (j) {
+                        printf(" ");
+                    }
+                    printf("\"%s\": %s", key->schema->name, key->value_str);
+                    key = (struct lyd_node_leaf_list *)key->next;
+                }
+                printf(")");
+            }
+            printf("\n");
+        }
+    }
+    printf("\n");
+
+    ly_set_free(set);
+    ret = 0;
+
+cleanup:
+    free(*argv);
+    free(argv);
+
+    lyd_free_withsiblings(data);
+
+    return ret;
+}
+
+int
+print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
+{
+    struct lyd_node *ylib;
+    uint32_t idx = 0, has_modules = 0;
+    uint8_t u;
+    const struct lys_module *mod;
+
+    if (outformat != LYD_UNKNOWN) {
+        ylib = ly_ctx_info(ctx);
+        if (!ylib) {
+            fprintf(stderr, "Getting context info (ietf-yang-library data) failed.\n");
+            return 1;
+        }
+
+        lyd_print_file(out, ylib, outformat, LYP_WITHSIBLINGS | LYP_FORMAT);
+        lyd_free_withsiblings(ylib);
+        return 0;
+    }
+
+    /* iterate schemas in context and provide just the basic info */
+    fprintf(out, "List of the loaded models:\n");
+    while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
+        has_modules++;
+
+        /* conformance print */
+        if (mod->implemented) {
+            fprintf(out, "\tI");
+        } else {
+            fprintf(out, "\ti");
+        }
+
+        /* module print */
+        fprintf(out, " %s", mod->name);
+        if (mod->rev_size) {
+            fprintf(out, "@%s", mod->rev[0].date);
+        }
+
+        /* submodules print */
+        if (mod->inc_size) {
+            fprintf(out, " (");
+            for (u = 0; u < mod->inc_size; u++) {
+                fprintf(out, "%s%s", !u ? "" : ",", mod->inc[u].submodule->name);
+                if (mod->inc[u].submodule->rev_size) {
+                    fprintf(out, "@%s", mod->inc[u].submodule->rev[0].date);
+                }
+            }
+            fprintf(out, ")");
+        }
+
+        /* finish the line */
+        fprintf(out, "\n");
+    }
+
+    if (!has_modules) {
+        fprintf(out, "\t(none)\n");
+    }
+
+    return 0;
+}
+
+int
+cmd_list(const char *arg)
+{
+    char **argv = NULL, *ptr;
+    int c, argc, option_index;
+    LYD_FORMAT outformat = LYD_UNKNOWN;
+    static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"format", required_argument, 0, 'f'},
+        {NULL, 0, 0, 0}
+    };
+    void *rlcd;
+
+    argc = 1;
+    argv = malloc(2*sizeof *argv);
+    *argv = strdup(arg);
+    ptr = strtok(*argv, " ");
+    while ((ptr = strtok(NULL, " "))) {
+        rlcd = realloc(argv, (argc+2)*sizeof *argv);
+        if (!rlcd) {
+            fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+            goto error;
+        }
+        argv = rlcd;
+        argv[argc++] = ptr;
+    }
+    argv[argc] = NULL;
+
+    optind = 0;
+    while (1) {
+        option_index = 0;
+        c = getopt_long(argc, argv, "hf:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'h':
+            cmd_data_help();
+            free(*argv);
+            free(argv);
+            return 0;
+        case 'f':
+            if (!strcmp(optarg, "xml")) {
+                outformat = LYD_XML;
+            } else if (!strcmp(optarg, "json")) {
+                outformat = LYD_JSON;
+            } else {
+                fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
+                goto error;
+            }
+            break;
+        case '?':
+            /* getopt_long() prints message */
+            goto error;
+        }
+    }
+    if (optind != argc) {
+        fprintf(stderr, "Unknown parameter \"%s\"\n", argv[optind]);
+error:
+        free(*argv);
+        free(argv);
+        return 1;
+    }
+    free(*argv);
+    free(argv);
+
+    return print_list(stdout, ctx, outformat);
+}
+#endif
+int
+cmd_feature(const char *arg)
+{
+    int c, argc, option_index, ret = 1, task = 0;
+    char **argv = NULL, *ptr, *model_name, *revision, *feat_names = NULL;
+    const struct lys_module *module;
+    static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"enable", required_argument, 0, 'e'},
+        {"disable", required_argument, 0, 'd'},
+        {NULL, 0, 0, 0}
+    };
+    void *rlcd;
+
+    argc = 1;
+    argv = malloc(2*sizeof *argv);
+    *argv = strdup(arg);
+    ptr = strtok(*argv, " ");
+    while ((ptr = strtok(NULL, " "))) {
+        rlcd = realloc(argv, (argc + 2) * sizeof *argv);
+        if (!rlcd) {
+            fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+            goto cleanup;
+        }
+        argv = rlcd;
+        argv[argc++] = ptr;
+    }
+    argv[argc] = NULL;
+
+    optind = 0;
+    while (1) {
+        option_index = 0;
+        c = getopt_long(argc, argv, "he:d:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'h':
+            cmd_feature_help();
+            ret = 0;
+            goto cleanup;
+        case 'e':
+            if (task) {
+                fprintf(stderr, "Only one of enable or disable can be specified.\n");
+                goto cleanup;
+            }
+            task = 1;
+            feat_names = optarg;
+            break;
+        case 'd':
+            if (task) {
+                fprintf(stderr, "Only one of enable, or disable can be specified.\n");
+                goto cleanup;
+            }
+            task = 2;
+            feat_names = optarg;
+            break;
+        case '?':
+            fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
+            goto cleanup;
+        }
+    }
+
+    /* module name */
+    if (optind == argc) {
+        fprintf(stderr, "Missing the module name.\n");
+        goto cleanup;
+    }
+
+    revision = NULL;
+    model_name = argv[optind];
+    if (strchr(model_name, '@')) {
+        revision = strchr(model_name, '@');
+        revision[0] = '\0';
+        ++revision;
+    }
+
+    module = ly_ctx_get_module(ctx, model_name, revision);
+#if 0
+    if (!module) {
+        /* not a module, try to find it as a submodule */
+        module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
+    }
+#endif
+
+    if (module == NULL) {
+        if (revision) {
+            fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
+        } else {
+            fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
+        }
+        goto cleanup;
+    }
+
+    if (!task) {
+        unsigned int len, max_len = 0;
+        unsigned int u;
+        struct lysc_feature *features;
+
+        printf("%s features:\n", module->name);
+
+        if (module->compiled) {
+            features = module->compiled->features;
+        } else {
+            features = module->off_features;
+        }
+
+        /* get the max len */
+        LY_ARRAY_FOR(features, u) {
+            len = strlen(features[u].name);
+            if (len > max_len) {
+                max_len = len;
+            }
+        }
+
+        LY_ARRAY_FOR(features, u) {
+            printf("\t%-*s (%s)\n", max_len, features[u].name, (features[u].flags & LYS_FENABLED) ? "on" : "off");
+        }
+        if (!u) {
+            printf("\t(none)\n");
+        }
+    } else {
+        feat_names = strtok(feat_names, ",");
+        while (feat_names) {
+            if (((task == 1) && lys_feature_enable(module, feat_names))
+                    || ((task == 2) && lys_feature_disable(module, feat_names))) {
+                fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
+                ret = 1;
+            }
+            feat_names = strtok(NULL, ",");
+        }
+    }
+
+cleanup:
+    free(*argv);
+    free(argv);
+
+    return ret;
+}
+
+int
+cmd_searchpath(const char *arg)
+{
+    const char *path;
+    const char * const *searchpaths;
+    int index;
+    struct stat st;
+
+    for (path = strchr(arg, ' '); path && (path[0] == ' '); ++path);
+    if (!path || (path[0] == '\0')) {
+        searchpaths = ly_ctx_get_searchdirs(ctx);
+        if (searchpaths) {
+            for (index = 0; searchpaths[index]; index++) {
+                fprintf(stdout, "%s\n", searchpaths[index]);
+            }
+        }
+        return 0;
+    }
+
+    if ((!strncmp(path, "-h", 2) && (path[2] == '\0' || path[2] == ' ')) ||
+        (!strncmp(path, "--help", 6) && (path[6] == '\0' || path[6] == ' '))) {
+        cmd_searchpath_help();
+        return 0;
+    } else if (!strncmp(path, "--clear", 7) && (path[7] == '\0' || path[7] == ' ')) {
+        ly_ctx_unset_searchdirs(ctx, NULL);
+        return 0;
+    }
+
+    if (stat(path, &st) == -1) {
+        fprintf(stderr, "Failed to stat the search path (%s).\n", strerror(errno));
+        return 1;
+    }
+    if (!S_ISDIR(st.st_mode)) {
+        fprintf(stderr, "\"%s\" is not a directory.\n", path);
+        return 1;
+    }
+
+    ly_ctx_set_searchdir(ctx, path);
+
+    return 0;
+}
+
+int
+cmd_clear(const char *arg)
+{
+    struct ly_ctx *ctx_new;
+    int options = 0;
+#if 0
+    int i;
+    char *ylpath;
+    const char * const *searchpaths;
+    LYD_FORMAT format;
+
+    /* get optional yang library file name */
+    for (i = 5; arg[i] && isspace(arg[i]); i++);
+    if (arg[i]) {
+        if (arg[i] == '-' && arg[i + 1] == 'e') {
+            options = LY_CTX_NOYANGLIBRARY;
+            goto create_empty;
+        } else {
+            ylpath = strdup(&arg[i]);
+            format = detect_data_format(ylpath);
+            if (format == LYD_UNKNOWN) {
+                free(ylpath);
+                fprintf(stderr, "Unable to resolve format of the yang library file, please add \".xml\" or \".json\" suffix.\n");
+                goto create_empty;
+            }
+            searchpaths = ly_ctx_get_searchdirs(ctx);
+            ctx_new = ly_ctx_new_ylpath(searchpaths ? searchpaths[0] : NULL, ylpath, format, 0);
+            free(ylpath);
+        }
+    } else {
+create_empty:
+#else
+    (void) arg; /* TODO unused */
+    {
+#endif
+        ly_ctx_new(NULL, options, &ctx_new);
+    }
+
+    if (!ctx_new) {
+        fprintf(stderr, "Failed to create context.\n");
+        return 1;
+    }
+
+    /* final switch */
+    ly_ctx_destroy(ctx, NULL);
+    ctx = ctx_new;
+
+    return 0;
+}
+
+int
+cmd_verb(const char *arg)
+{
+    const char *verb;
+    if (strlen(arg) < 5) {
+        cmd_verb_help();
+        return 1;
+    }
+
+    verb = arg + 5;
+    if (!strcmp(verb, "error") || !strcmp(verb, "0")) {
+        ly_verb(LY_LLERR);
+#ifndef NDEBUG
+        ly_verb_dbg(0);
+#endif
+    } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) {
+        ly_verb(LY_LLWRN);
+#ifndef NDEBUG
+        ly_verb_dbg(0);
+#endif
+    } else if (!strcmp(verb, "verbose")  || !strcmp(verb, "2")) {
+        ly_verb(LY_LLVRB);
+#ifndef NDEBUG
+        ly_verb_dbg(0);
+#endif
+    } else if (!strcmp(verb, "debug")  || !strcmp(verb, "3")) {
+        ly_verb(LY_LLDBG);
+#ifndef NDEBUG
+        ly_verb_dbg(LY_LDGDICT | LY_LDGYANG | LY_LDGYIN | LY_LDGXPATH | LY_LDGDIFF);
+#endif
+    } else {
+        fprintf(stderr, "Unknown verbosity \"%s\"\n", verb);
+        return 1;
+    }
+
+    return 0;
+}
+
+#ifndef NDEBUG
+
+int
+cmd_debug(const char *arg)
+{
+    const char *beg, *end;
+    int grps = 0;
+    if (strlen(arg) < 6) {
+        cmd_debug_help();
+        return 1;
+    }
+
+    end = arg + 6;
+    while (end[0]) {
+        for (beg = end; isspace(beg[0]); ++beg);
+        if (!beg[0]) {
+            break;
+        }
+
+        for (end = beg; (end[0] && !isspace(end[0])); ++end);
+
+        if (!strncmp(beg, "dict", end - beg)) {
+            grps |= LY_LDGDICT;
+        } else if (!strncmp(beg, "yang", end - beg)) {
+            grps |= LY_LDGYANG;
+        } else if (!strncmp(beg, "yin", end - beg)) {
+            grps |= LY_LDGYIN;
+        } else if (!strncmp(beg, "xpath", end - beg)) {
+            grps |= LY_LDGXPATH;
+        } else if (!strncmp(beg, "diff", end - beg)) {
+            grps |= LY_LDGDIFF;
+        } else {
+            fprintf(stderr, "Unknown debug group \"%.*s\"\n", (int)(end - beg), beg);
+            return 1;
+        }
+    }
+    ly_verb_dbg(grps);
+
+    return 0;
+}
+
+#endif
+
+int
+cmd_quit(const char *UNUSED(arg))
+{
+    done = 1;
+    return 0;
+}
+
+int
+cmd_help(const char *arg)
+{
+    int i;
+    char *args = strdup(arg);
+    char *cmd = NULL;
+
+    strtok(args, " ");
+    if ((cmd = strtok(NULL, " ")) == NULL) {
+
+generic_help:
+        fprintf(stdout, "Available commands:\n");
+
+        for (i = 0; commands[i].name; i++) {
+            if (commands[i].helpstring != NULL) {
+                fprintf(stdout, "  %-15s %s\n", commands[i].name, commands[i].helpstring);
+            }
+        }
+    } else {
+        /* print specific help for the selected command */
+
+        /* get the command of the specified name */
+        for (i = 0; commands[i].name; i++) {
+            if (strcmp(cmd, commands[i].name) == 0) {
+                break;
+            }
+        }
+
+        /* execute the command's help if any valid command specified */
+        if (commands[i].name) {
+            if (commands[i].help_func != NULL) {
+                commands[i].help_func();
+            } else {
+                printf("%s\n", commands[i].helpstring);
+            }
+        } else {
+            /* if unknown command specified, print the list of commands */
+            printf("Unknown command \'%s\'\n", cmd);
+            goto generic_help;
+        }
+    }
+
+    free(args);
+    return 0;
+}
+
+COMMAND commands[] = {
+        {"help", cmd_help, NULL, "Display commands description"},
+        {"add", cmd_add, cmd_add_help, "Add a new model from a specific file"},
+        {"load", cmd_load, cmd_load_help, "Load a new model from the searchdirs"},
+        {"print", cmd_print, cmd_print_help, "Print a model"},
+        {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
+#if 0
+        {"xpath", cmd_xpath, cmd_xpath_help, "Get data nodes satisfying an XPath expression"},
+        {"list", cmd_list, cmd_list_help, "List all the loaded models"},
+#endif
+        {"feature", cmd_feature, cmd_feature_help, "Print/enable/disable all/specific features of models"},
+        {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for models"},
+        {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded models"},
+        {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
+#ifndef NDEBUG
+        {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
+#endif
+        {"quit", cmd_quit, NULL, "Quit the program"},
+        /* synonyms for previous commands */
+        {"?", cmd_help, NULL, "Display commands description"},
+        {"exit", cmd_quit, NULL, "Quit the program"},
+        {NULL, NULL, NULL, NULL}
+};
diff --git a/tools/lint/commands.h b/tools/lint/commands.h
new file mode 100644
index 0000000..18d7fdc
--- /dev/null
+++ b/tools/lint/commands.h
@@ -0,0 +1,46 @@
+/**
+ * @file main.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang's yanglint tool commands header
+ *
+ * Copyright (c) 2015 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 COMMANDS_H_
+#define COMMANDS_H_
+
+#ifdef __GNUC__
+#  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+#else
+#  define UNUSED(x) UNUSED_ ## x
+#endif
+
+#include <stdlib.h>
+
+#include "libyang.h"
+
+#define PROMPT "> "
+
+struct model_hint {
+	char* hint;
+	struct model_hint* next;
+};
+
+typedef struct {
+	char *name; /* User printable name of the function. */
+	int (*func)(const char*); /* Function to call to do the command. */
+	void (*help_func)(void); /* Display command help. */
+	char *helpstring; /* Documentation for this function. */
+} COMMAND;
+
+LYS_INFORMAT get_schema_format(const char *path);
+
+extern COMMAND commands[];
+
+#endif /* COMMANDS_H_ */
diff --git a/tools/lint/completion.c b/tools/lint/completion.c
new file mode 100644
index 0000000..f2e8461
--- /dev/null
+++ b/tools/lint/completion.c
@@ -0,0 +1,140 @@
+/**
+ * @file completion.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang's yanglint tool auto completion
+ *
+ * Copyright (c) 2015 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 "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+
+#include "commands.h"
+#include "./linenoise/linenoise.h"
+#include "libyang.h"
+
+extern struct ly_ctx *ctx;
+
+static void
+get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count)
+{
+    int i;
+    void *p;
+
+    *match_count = 0;
+    *matches = NULL;
+
+    for (i = 0; commands[i].name; i++) {
+        if (!strncmp(hint, commands[i].name, strlen(hint))) {
+            ++(*match_count);
+            p = realloc(*matches, *match_count * sizeof **matches);
+            if (!p) {
+                fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+                return;
+            }
+            *matches = p;
+            (*matches)[*match_count-1] = strdup(commands[i].name);
+        }
+    }
+}
+
+static int
+last_is_opt(const char *hint)
+{
+    const char *ptr;
+
+    /* last is option */
+    if (hint[0] == '-') {
+        return 1;
+    }
+
+    do {
+        --hint;
+    } while (hint[0] == ' ');
+
+    /* last is option argument */
+    ptr = strrchr(hint, ' ');
+    if (ptr) {
+        ++ptr;
+        if (ptr[0] == '-') {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static void
+get_model_completion(const char *hint, char ***matches, unsigned int *match_count)
+{
+    unsigned int u;
+    uint32_t idx = 0;
+    const struct lys_module *module;
+    void *p;
+
+    *match_count = 0;
+    *matches = NULL;
+
+    while ((module = ly_ctx_get_module_iter(ctx, &idx))) {
+        if (!strncmp(hint, module->name, strlen(hint))) {
+            ++(*match_count);
+            p = realloc(*matches, *match_count * sizeof **matches);
+            if (!p) {
+                fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+                return;
+            }
+            *matches = p;
+            (*matches)[*match_count-1] = strdup(module->name);
+        }
+
+        LY_ARRAY_FOR(module->parsed->includes, u) {
+            if (!strncmp(hint, module->parsed->includes[u].submodule->name, strlen(hint))) {
+                ++(*match_count);
+                p = realloc(*matches, *match_count * sizeof **matches);
+                if (!p) {
+                    fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+                    return;
+                }
+                *matches = p;
+                (*matches)[*match_count-1] = strdup(module->parsed->includes[u].submodule->name);
+            }
+        }
+    }
+}
+
+void
+complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
+{
+    char **matches = NULL;
+    unsigned int match_count = 0, i;
+
+    if (!strncmp(buf, "add ", 4)) {
+        linenoisePathCompletion(buf, hint, lc);
+    } else if ((!strncmp(buf, "searchpath ", 11) || !strncmp(buf, "data ", 5)
+            || !strncmp(buf, "config ", 7) || !strncmp(buf, "filter ", 7)
+            || !strncmp(buf, "xpath ", 6) || !strncmp(buf, "clear ", 6)) && !last_is_opt(hint)) {
+        linenoisePathCompletion(buf, hint, lc);
+    } else if ((!strncmp(buf, "print ", 6) || !strncmp(buf, "feature ", 8)) && !last_is_opt(hint)) {
+        get_model_completion(hint, &matches, &match_count);
+    } else if (!strchr(buf, ' ') && hint[0]) {
+        get_cmd_completion(hint, &matches, &match_count);
+    }
+
+    for (i = 0; i < match_count; ++i) {
+        linenoiseAddCompletion(lc, matches[i]);
+        free(matches[i]);
+    }
+    free(matches);
+}
diff --git a/tools/lint/completion.h b/tools/lint/completion.h
new file mode 100644
index 0000000..953add2
--- /dev/null
+++ b/tools/lint/completion.h
@@ -0,0 +1,22 @@
+/**
+ * @file main.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang's yanglint tool auto completion header
+ *
+ * Copyright (c) 2015 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 COMPLETION_H_
+#define COMPLETION_H_
+
+#include "./linenoise/linenoise.h"
+
+void complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc);
+
+#endif /* COMPLETION_H_ */
diff --git a/tools/lint/configuration.c b/tools/lint/configuration.c
new file mode 100644
index 0000000..095d2d0
--- /dev/null
+++ b/tools/lint/configuration.c
@@ -0,0 +1,121 @@
+/**
+ * @file configuration.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief yanglint configuration
+ *
+ * Copyright (c) 2017 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 "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "configuration.h"
+#include "./linenoise/linenoise.h"
+
+/* Yanglint home (appended to ~/) */
+#define YL_DIR ".yanglint"
+
+char *
+get_yanglint_dir(void)
+{
+    int ret;
+    struct passwd *pw;
+    char *user_home, *yl_dir;
+
+    if (!(pw = getpwuid(getuid()))) {
+        fprintf(stderr, "Determining home directory failed (%s).\n", strerror(errno));
+        return NULL;
+    }
+    user_home = pw->pw_dir;
+
+    yl_dir = malloc(strlen(user_home) + 1 + strlen(YL_DIR) + 1);
+    if (!yl_dir) {
+        fprintf(stderr, "Memory allocation failed (%s).\n", strerror(errno));
+        return NULL;
+    }
+    sprintf(yl_dir, "%s/%s", user_home, YL_DIR);
+
+    ret = access(yl_dir, R_OK | X_OK);
+    if (ret == -1) {
+        if (errno == ENOENT) {
+            /* directory does not exist */
+            fprintf(stdout, "Configuration directory \"%s\" does not exist, creating it.\n", yl_dir);
+            if (mkdir(yl_dir, 00700)) {
+                fprintf(stderr, "Configuration directory \"%s\" cannot be created (%s).\n", yl_dir, strerror(errno));
+                free(yl_dir);
+                return NULL;
+            }
+        } else {
+            fprintf(stderr, "Configuration directory \"%s\" exists but cannot be accessed (%s).\n", yl_dir, strerror(errno));
+            free(yl_dir);
+            return NULL;
+        }
+    }
+
+    return yl_dir;
+}
+
+void
+load_config(void)
+{
+    char *yl_dir, *history_file;
+    if ((yl_dir = get_yanglint_dir()) == NULL) {
+        return;
+    }
+
+    history_file = malloc(strlen(yl_dir) + 9);
+    if (!history_file) {
+        fprintf(stderr, "Memory allocation failed (%s).\n", strerror(errno));
+        free(yl_dir);
+        return;
+    }
+
+    sprintf(history_file, "%s/history", yl_dir);
+    if (access(history_file, F_OK) && (errno == ENOENT)) {
+        fprintf(stdout, "No saved history.\n");
+    } else if (linenoiseHistoryLoad(history_file)) {
+        fprintf(stderr, "Failed to load history.\n");
+    }
+
+    free(history_file);
+    free(yl_dir);
+}
+
+void
+store_config(void)
+{
+    char *yl_dir, *history_file;
+
+    if ((yl_dir = get_yanglint_dir()) == NULL) {
+        return;
+    }
+
+    history_file = malloc(strlen(yl_dir) + 9);
+    if (!history_file) {
+        fprintf(stderr, "Memory allocation failed (%s).\n", strerror(errno));
+        free(yl_dir);
+        return;
+    }
+
+    sprintf(history_file, "%s/history", yl_dir);
+    if (linenoiseHistorySave(history_file)) {
+        fprintf(stderr, "Failed to save history.\n");
+    }
+
+    free(history_file);
+    free(yl_dir);
+}
diff --git a/tools/lint/configuration.h b/tools/lint/configuration.h
new file mode 100644
index 0000000..a299407
--- /dev/null
+++ b/tools/lint/configuration.h
@@ -0,0 +1,37 @@
+/**
+ * @file configuration.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief yanglint configuration header
+ *
+ * Copyright (c) 2017 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 CONFIGURATION_H_
+#define CONFIGURATION_H_
+
+
+/**
+ * @brief Finds the current user's yanglint dir
+ * @return NULL on failure, dynamically allocated yanglint dir path
+ * otherwise
+ */
+char *get_yanglint_dir(void);
+
+/**
+ * @brief Checks all the relevant files and directories creating any
+ * that are missing, sets the saved configuration (currently only history)
+ */
+void load_config(void);
+
+/**
+ * @brief Saves the current configuration (currently only history)
+ */
+void store_config(void);
+
+#endif /* CONFIGURATION_H_ */
diff --git a/tools/lint/examples/README.md b/tools/lint/examples/README.md
new file mode 100644
index 0000000..4b1d972
--- /dev/null
+++ b/tools/lint/examples/README.md
@@ -0,0 +1,572 @@
+YANGLINT IS NOT YET PREPARED FOR USE, EXAMPLES MAY NOT WORK YET!!!
+
+# YANGLINT - Interactive Mode Examples
+
+This text provides several use-case of the `yanglint(1)` interactive
+mode. For basic information about the `yanglint(1)` usage, please see
+the man page.
+
+The examples are supposed to be went through one by one. Some of the examples
+suppose the specific schemas loaded in some of the previous example is still
+loaded. If an addition work is need, the *preparation* part in the example
+provides information what to do.
+
+To show all available command of the `yanglint(1)`, use the `help` command:
+```
+> help
+Available commands:
+  help            Display commands description
+  add             Add a new model
+  print           Print model
+  data            Load, validate and optionally print instance data
+  xpath           Get data nodes satisfying an XPath expression
+  list            List all the loaded models
+  feature         Print/enable/disable all/specific features of models
+  searchpath      Set the search path for models
+  clear           Clear the context - remove all the loaded models
+  verb            Change verbosity
+  quit            Quit the program
+  ?               Display commands description
+  exit            Quit the program
+```
+To show the information about the specific command, use the `help` command in
+combination with the command name you are interested in:
+```
+> help searchpath
+searchpath <model-dir-path>
+```
+
+The input files referred in this document are available together with this
+document.
+
+## Duplicit Data Model
+
+Let's have two data models [module1.yang](./module1.yang)
+and [module1b.yang](./module1b.yang).
+They differ in the module name but their namespaces are the same.
+
+Preparation:
+
+```
+> clear
+> add module1.yang
+> list
+```
+
+Output:
+
+```
+List of the loaded models (mod-set-id 5):
+        ietf-inet-types@2013-07-15
+        ietf-yang-types@2013-07-15
+        ietf-yang-library@2015-07-03
+        module1
+```
+
+Command and its output:
+
+```
+> add module1b.yang
+libyang[0]: Two different modules ("module1" and "module1b") have the same namespace "urn:yanglint:module".
+libyang[0]: Module "module1b" parsing failed.
+```
+
+## Yang Data Model Validation
+
+**YANG/YIN syntax**
+
+`module2.yin` contains a syntax error.
+There is a bad syntax of the `type` statement in YIN file.
+
+```
+<type value="string"/>
+```
+
+instead of
+
+```
+<type name="string"/>
+```
+
+Preparation:
+
+```
+> clear
+```
+
+Command and its output:
+
+```
+> add module2.yin
+libyang[0]: Missing argument "name" to keyword "type".
+libyang[0]: Module "module1" parsing failed.
+```
+
+Similarly, there is a typo in `module2.yang`.
+
+**XPath errors**
+
+`libyang` and `yanglint(1)` is able to detect also errors in XPath expressions.
+In `module3.yang` the `must` expression refers to the node which does not exists.
+
+Preparation:
+
+```
+> clear
+```
+
+Command and its output:
+
+```
+> add module3.yang
+libyang[0]: Schema node "a" not found (../c/a).
+libyang[0]: Path is related to the previous error message. (path: /module3:m)
+libyang[0]: Module "module3" parsing failed.
+```
+
+Note that libyang does not provide line numbers of the error. Instead it tries to
+print the path to the related node. in some cases (as this one) it is not able
+to print the path immediately so the path (to the node `m` which refers node which
+does not exist) is printed in the second message.
+
+## Data Validation
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm.yang
+```
+
+**Unknown data**
+
+By default, yanglint ignores unknown data and no error is printed (you can
+compare real content of the `datastore.xml` file and what yanglint prints
+in the following command if you add `-f xml` option).
+
+Command and its output:
+
+```
+> data -t config datastore.xml
+```
+
+We use option `-t` to specify type of the data in `datastore.xml`. By the
+`config` value we declare that the input file contains all the configuration
+data (with at least all the mandatory nodes as required by the loaded schemas),
+but without the status data. More examples of different data types will follow.
+
+To handle unknown data as error, use strict mode (`-s` option).
+
+Command and its output:
+
+```
+> data -t config -s datastore.xml
+libyang[0]: Unknown element "interfaces". (path: /)
+Failed to parse data.
+```
+
+Note that in case of working with complete datastore including the status data
+(no `-t` option is specified), `yanglint(1)` has to add status data from its
+internal `ietf-yang-library` module. Using the `-s` option in this case forces
+validation in time of parsing the input file so it is expected to include also
+the mandatory status data from the `ietf-yang-library` module.
+
+**RPC and RPC-reply**
+
+It is possible to validate RPCs and their replies as well.
+
+Peparation:
+
+```
+> clear
+> add module4.yang
+```
+
+Command and its output:
+
+```
+> data -t rpc rpc.xml
+```
+
+Reply to this RPC can be validated too, but it must be specified, to which
+RPC it is a reply to, because it is not included in the reply itself.
+
+Command and its output:
+
+```
+> data -t rpcreply rpc-reply.xml rpc.xml
+```
+
+**action and action-reply**
+
+Actions are validated the same way as RPCs except you need to be careful
+about the input file structure.
+
+Preparation
+
+```
+> clear
+> add module4.yang
+```
+
+Command and its output:
+
+```
+> data -t rpc action.xml
+```
+
+Command and its output:
+
+```
+> data -t rpc action-reply.xml action.xml
+```
+
+**notification**
+
+Both top-level and nested notification can be validated.
+
+Preparation
+
+```
+> clear
+> add module4.yang
+```
+
+Command and its output:
+
+```
+> data -t notif notification.xml
+```
+
+Command and its output:
+
+```
+> data -t notif nested-notification.xml
+```
+
+
+**Multiple top-level elements in a single document**
+
+As a feature and in conflict with the XML definition, `yanglint(1)` (and libyang)
+is able to read XML files with multiple top-level elements. Such documents
+are not well-formed according to the XML spec, but it fits to how the YANG
+interconnects data trees (defined as top-level elements of a single schema
+or by multiple schemas).
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm.yang
+> add ietf-interfaces.yang
+> add ietf-ip.yang
+> add iana-if-type.yang
+```
+
+Command and its output:
+
+```
+> data -t config -s datastore.xml
+```
+
+**Different data content types**
+
+Since NETCONF requires the data described by YANG to be used in different
+situations (e.g. as <edit-config data>, result of the <get> with status data
+included or as a result of the <get-config> without the status data and
+possibly filtered, so without specified subtrees), it must be possible to
+specify which kind of data is going to be parsed. In `yanglint(1)`, this is done
+via `-t` option. The list of supported modes can be displayed by the `-h`
+option given to the `data` command. In general, the `auto` value lets the
+`yanglint(1)` to recognize the data type automatically by the additional top-level
+elements added to the parsed data. This is the same way as `pyang(1)` uses. Note,
+that the automatic data type recognition is available only for the XML input.
+
+**Malformed XML data**
+
+Command and its output:
+
+```
+> data -t edit config-missing-key.xml
+libyang[0]: Invalid (mixed names) opening (nam) and closing (name) element tags. (path: /nacm/groups/group/nam)
+Failed to parse data.
+```
+
+**State information in edit-config XML**
+
+Command and its output:
+
+```
+> data -t edit config-unknown-element.xml
+libyang[0]: Unknown element "denied-operations". (path: /ietf-netconf-acm:nacm/denied-operations)
+Failed to parse data.
+```
+
+**Missing required element in NETCONF data**
+
+Command and its output:
+
+```
+> data data-missing-key.xml
+libyang[0]: Missing required element "name" in "rule". (path: /ietf-netconf-acm:nacm/rule-list[name='almighty']/rule)
+Failed to parse data.
+```
+
+**Malformed XML**
+
+Command and its output:
+
+```
+> data data-malformed-xml.xml
+libyang[0]: Invalid (mixed names) opening (nam) and closing (rule) element tags. (path: /nacm/rule-list/rule/nam)
+Failed to parse data.
+```
+
+Command and its output:
+
+```
+> data data-malformed-xml2.xml
+libyang[0]: Invalid (mixed names) opening (module-name) and closing (name) element tags. (path: /nacm/rule-list/rule/name/module-name)
+Failed to parse data.
+```
+
+**Bad value**
+
+Command and its output:
+
+```
+> data data-out-of-range-value.xml
+libyang[0]: Invalid value "-1" in "denied-operations" element. (path: /ietf-netconf-acm:nacm/denied-operations)
+Failed to parse data.
+```
+
+## Validation of "when" Statement in Data
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm-when.yang
+```
+
+**`When` condition is not satisfied since `denied-operation = 0`**
+
+Command and its output:
+
+```
+> data data-acm.xml
+```
+
+The command succeeds. It is because `yanglint(1)` (via `libyang`) performs
+autodeletion - the not satisfied `when` condition in `denied-data-writes`
+causes its automatic (silent) deletion.
+
+## Printing a Data Model
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm.yang
+```
+
+**Print a `pyang`-style tree**
+
+Command and its output:
+
+```
+> print ietf-netconf-acm
+module: ietf-netconf-acm
+   +--rw nacm
+      +--rw enable-nacm?              boolean <true>
+      +--rw read-default?             action-type <permit>
+      +--rw write-default?            action-type <deny>
+      +--rw exec-default?             action-type <permit>
+      +--rw enable-external-groups?   boolean <true>
+      +--ro denied-operations         ietf-yang-types:zero-based-counter32
+      +--ro denied-data-writes        ietf-yang-types:zero-based-counter32
+      +--ro denied-notifications      ietf-yang-types:zero-based-counter32
+      +--rw groups
+      |  +--rw group* [name]
+      |     +--rw name         group-name-type
+      |     +--rw user-name*   user-name-type
+      +--rw rule-list* [name]
+         +--rw name     string
+         +--rw group*   union
+         +--rw rule* [name]
+            +--rw name                 string
+            +--rw module-name?         union <*>
+            +--rw (rule-type)?
+            |  +--:(protocol-operation)
+            |  |  +--rw rpc-name?             union
+            |  +--:(notification)
+            |  |  +--rw notification-name?    union
+            |  +--:(data-node)
+            |     +--rw path                  node-instance-identifier
+            +--rw access-operations?   union <*>
+            +--rw action               action-type
+            +--rw comment?             string
+>
+```
+
+**Obtain information about model**
+
+Command and its output:
+
+```
+> print -f info ietf-netconf-acm
+Module:    ietf-netconf-acm
+Namespace: urn:ietf:params:xml:ns:yang:ietf-netconf-acm
+Prefix:    nacm
+Desc:      NETCONF Access Control Model.
+
+           Copyright (c) 2012 IETF Trust and the persons identified as
+           authors of the code.  All rights reserved.
+
+           Redistribution and use in source and binary forms, with or
+           without modification, is permitted pursuant to, and subject
+           to the license terms contained in, the Simplified BSD
+           License set forth in Section 4.c of the IETF Trust's
+           Legal Provisions Relating to IETF Documents
+           (http://trustee.ietf.org/license-info).
+
+           This version of this YANG module is part of RFC 6536; see
+           the RFC itself for full legal notices.
+Reference:
+Org:       IETF NETCONF (Network Configuration) Working Group
+Contact:   WG Web:   <http://tools.ietf.org/wg/netconf/>
+           WG List:  <mailto:netconf@ietf.org>
+
+           WG Chair: Mehmet Ersue
+                     <mailto:mehmet.ersue@nsn.com>
+
+           WG Chair: Bert Wijnen
+                     <mailto:bertietf@bwijnen.net>
+
+           Editor:   Andy Bierman
+                     <mailto:andy@yumaworks.com>
+
+           Editor:   Martin Bjorklund
+                     <mailto:mbj@tail-f.com>
+YANG ver:  1.0
+Deviated:  no
+Implement: yes
+URI:
+Revisions: 2012-02-22
+Includes:
+Imports:   yang:ietf-yang-types
+Typedefs:  user-name-type
+           matchall-string-type
+           access-operations-type
+           group-name-type
+           action-type
+           node-instance-identifier
+Idents:
+Features:
+Augments:
+Deviation:
+Data:      container "nacm"
+```
+
+**Print information about specific model part**
+
+Command and its output:
+
+```
+> print -f info -t /ietf-netconf-acm:nacm/ietf-netconf-acm:enable-nacm ietf-netconf-ac
+Leaf:      enable-nacm
+Module:    ietf-netconf-acm
+Desc:      Enables or disables all NETCONF access control
+           enforcement.  If 'true', then enforcement
+           is enabled.  If 'false', then enforcement
+           is disabled.
+Reference:
+Config:    read-write
+Status:    current
+Mandatory: no
+Type:      boolean
+Units:
+Default:   true
+If-feats:
+When:
+Must:
+NACM:      default-deny-all
+```
+
+## Query using NETCONF data
+
+Preparation:
+
+```
+> clear
+> add ietf-netconf-acm.yang
+```
+
+**Print all `user-name` elements that occure in data**
+
+Command and its output:
+
+```
+> xpath -e //ietf-netconf-acm:user-name data-acm.xml
+Result:
+        Leaflist "user-name" (val: smith)
+        Leaflist "user-name" (val: smith)
+        Leaflist "user-name" (val: doe)
+
+```
+
+**Print all data that satisfies condition**
+
+Command and its output:
+
+```
+> xpath -e //ietf-netconf-acm:user-name[text()="smith"] data-acm.xml
+Result:
+        Leaflist "user-name" (val: smith)
+        Leaflist "user-name" (val: smith)
+
+```
+
+## Usage of `feature` in Yang
+
+Preparation:
+
+```
+> clear
+> add ietf-interfaces.yang
+> add ietf-ip.yang
+> add iana-if-type.yang
+```
+
+Note: This example also shows `JSON` output of the command.
+
+Command and its output:
+```
+> feature -e * ietf-ip
+> data -f json -t config data-ip.xml
+{
+  "ietf-interfaces:interfaces": {
+    "interface": [
+      {
+        "name": "eth0",
+        "description": "Wire Connection",
+        "type": "iana-if-type:ethernetCsmacd",
+        "enabled": true,
+        "ietf-ip:ipv4": {
+          "address": [
+            {
+              "ip": "192.168.1.15",
+              "netmask": "255.255.255.0"
+            },
+            {
+              "ip": "192.168.1.10",
+              "netmask": "255.255.255.0"
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
+```
+
diff --git a/tools/lint/examples/action-reply.xml b/tools/lint/examples/action-reply.xml
new file mode 100644
index 0000000..5a4788e
--- /dev/null
+++ b/tools/lint/examples/action-reply.xml
@@ -0,0 +1 @@
+<leaf3 xmlns="urn:module4">some_output</leaf3>
diff --git a/tools/lint/examples/action.xml b/tools/lint/examples/action.xml
new file mode 100644
index 0000000..b0bd411
--- /dev/null
+++ b/tools/lint/examples/action.xml
@@ -0,0 +1,10 @@
+<action xmlns="urn:ietf:params:xml:ns:yang:1">
+  <cont1 xmlns="urn:module4">
+    <list>
+      <leaf1>key_val</leaf1>
+      <act>
+        <leaf2>some_input</leaf2>
+      </act>
+    </list>
+  </cont1>
+</action>
diff --git a/tools/lint/examples/config-acm.xml b/tools/lint/examples/config-acm.xml
new file mode 100644
index 0000000..8c99419
--- /dev/null
+++ b/tools/lint/examples/config-acm.xml
@@ -0,0 +1,24 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <group nc:operation="create">test</group>
+    <rule>
+      <name>almighty</name>
+      <module-name>*</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>test</name>
+      <user-name>smith</user-name>
+    </group>
+    <group>
+      <name>almighty</name>
+      <user-name>smith</user-name>
+      <user-name>doe</user-name>
+    </group>
+  </groups>
+</nacm>
diff --git a/tools/lint/examples/config-missing-key.xml b/tools/lint/examples/config-missing-key.xml
new file mode 100644
index 0000000..16854d5
--- /dev/null
+++ b/tools/lint/examples/config-missing-key.xml
@@ -0,0 +1,24 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <group nc:operation="create">test</group>
+    <rule>
+      <name>almighty</name>
+      <module-name>*</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>test</name>
+      <user-name>smith</user-name>
+    </group>
+    <group>
+      <nam>almighty</name>
+      <user-name>smith</user-name>
+      <user-name>doe</user-name>
+    </group>
+  </groups>
+</nacm>
diff --git a/tools/lint/examples/config-unknown-element.xml b/tools/lint/examples/config-unknown-element.xml
new file mode 100644
index 0000000..87c79ee
--- /dev/null
+++ b/tools/lint/examples/config-unknown-element.xml
@@ -0,0 +1,27 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <group nc:operation="create">test</group>
+    <rule>
+      <name>almighty</name>
+      <module-name>*</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>test</name>
+      <user-name>smith</user-name>
+    </group>
+    <group>
+      <name>almighty</name>
+      <user-name>smith</user-name>
+      <user-name>doe</user-name>
+    </group>
+  </groups>
+  <denied-operations>0</denied-operations>
+  <denied-data-writes>0</denied-data-writes>
+  <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-acm.xml b/tools/lint/examples/data-acm.xml
new file mode 100644
index 0000000..87c79ee
--- /dev/null
+++ b/tools/lint/examples/data-acm.xml
@@ -0,0 +1,27 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <group nc:operation="create">test</group>
+    <rule>
+      <name>almighty</name>
+      <module-name>*</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>test</name>
+      <user-name>smith</user-name>
+    </group>
+    <group>
+      <name>almighty</name>
+      <user-name>smith</user-name>
+      <user-name>doe</user-name>
+    </group>
+  </groups>
+  <denied-operations>0</denied-operations>
+  <denied-data-writes>0</denied-data-writes>
+  <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-ip.xml b/tools/lint/examples/data-ip.xml
new file mode 100644
index 0000000..1894f6d
--- /dev/null
+++ b/tools/lint/examples/data-ip.xml
@@ -0,0 +1,12 @@
+<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
+  <interface>
+    <name>eth0</name>
+    <description>Wire Connection</description>
+    <type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
+    <enabled>true</enabled>
+    <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
+      <address><ip>192.168.1.15</ip><netmask>255.255.255.0</netmask></address>
+      <address><ip>192.168.1.10</ip><netmask>255.255.255.0</netmask></address>
+    </ipv4>
+  </interface>
+</interfaces>
diff --git a/tools/lint/examples/data-malformed-xml.xml b/tools/lint/examples/data-malformed-xml.xml
new file mode 100644
index 0000000..fd800ad
--- /dev/null
+++ b/tools/lint/examples/data-malformed-xml.xml
@@ -0,0 +1,27 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <group nc:operation="create">test</group>
+    <rule>
+      <nam>almighty
+      <module-name>*</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>test</name>
+      <user-name>smith</user-name>
+    </group>
+    <group>
+      <name>almighty</name>
+      <user-name>smith</user-name>
+      <user-name>doe</user-name>
+    </group>
+  </groups>
+  <denied-operations>0</denied-operations>
+  <denied-data-writes>0</denied-data-writes>
+  <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-malformed-xml2.xml b/tools/lint/examples/data-malformed-xml2.xml
new file mode 100644
index 0000000..1837264
--- /dev/null
+++ b/tools/lint/examples/data-malformed-xml2.xml
@@ -0,0 +1,26 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <group nc:operation="create">test</group>
+    <rule>
+      <name>almighty<module-name></name> *</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>test</name>
+      <user-name>smith</user-name>
+    </group>
+    <group>
+      <name>almighty</name>
+      <user-name>smith</user-name>
+      <user-name>doe</user-name>
+    </group>
+  </groups>
+  <denied-operations>0</denied-operations>
+  <denied-data-writes>0</denied-data-writes>
+  <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-missing-key.xml b/tools/lint/examples/data-missing-key.xml
new file mode 100644
index 0000000..d3ccd53
--- /dev/null
+++ b/tools/lint/examples/data-missing-key.xml
@@ -0,0 +1,26 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <group nc:operation="create">test</group>
+    <rule>
+      <module-name>*</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>test</name>
+      <user-name>smith</user-name>
+    </group>
+    <group>
+      <name>almighty</name>
+      <user-name>smith</user-name>
+      <user-name>doe</user-name>
+    </group>
+  </groups>
+  <denied-operations>0</denied-operations>
+  <denied-data-writes>0</denied-data-writes>
+  <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/data-out-of-range-value.xml b/tools/lint/examples/data-out-of-range-value.xml
new file mode 100644
index 0000000..83a0d65
--- /dev/null
+++ b/tools/lint/examples/data-out-of-range-value.xml
@@ -0,0 +1,27 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <group nc:operation="create">test</group>
+    <rule>
+      <name>almighty</name>
+      <module-name>*</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>test</name>
+      <user-name>smith</user-name>
+    </group>
+    <group>
+      <name>almighty</name>
+      <user-name>smith</user-name>
+      <user-name>doe</user-name>
+    </group>
+  </groups>
+  <denied-operations>-1</denied-operations>
+  <denied-data-writes>0</denied-data-writes>
+  <denied-notifications>0</denied-notifications>
+</nacm>
diff --git a/tools/lint/examples/datastore.xml b/tools/lint/examples/datastore.xml
new file mode 100644
index 0000000..c6a6fc9
--- /dev/null
+++ b/tools/lint/examples/datastore.xml
@@ -0,0 +1,29 @@
+<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
+  <rule-list>
+    <name>almighty</name>
+    <group>almighty</group>
+    <rule>
+      <name>almighty</name>
+      <module-name>*</module-name>
+      <access-operations>*</access-operations>
+      <action>permit</action>
+    </rule>
+  </rule-list>
+  <groups>
+    <group>
+      <name>almighty</name>
+      <user-name>smith</user-name>
+    </group>
+  </groups>
+</nacm>
+<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
+  <interface>
+    <name>eth0</name>
+    <description>Wire Connection</description>
+    <type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
+    <enabled>true</enabled>
+    <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
+      <address><ip>192.168.1.15</ip><prefix-length>24</prefix-length></address>
+    </ipv4>
+  </interface>
+</interfaces>
diff --git a/tools/lint/examples/iana-if-type.yang b/tools/lint/examples/iana-if-type.yang
new file mode 100644
index 0000000..5dd8219
--- /dev/null
+++ b/tools/lint/examples/iana-if-type.yang
@@ -0,0 +1,1547 @@
+module iana-if-type {
+  namespace "urn:ietf:params:xml:ns:yang:iana-if-type";
+  prefix ianaift;
+
+  import ietf-interfaces {
+    prefix if;
+  }
+
+  organization "IANA";
+  contact
+    "        Internet Assigned Numbers Authority
+
+     Postal: ICANN
+             4676 Admiralty Way, Suite 330
+             Marina del Rey, CA 90292
+
+     Tel:    +1 310 823 9358
+     <mailto:iana@iana.org>";
+  description
+    "This YANG module defines YANG identities for IANA-registered
+     interface types.
+
+     This YANG module is maintained by IANA and reflects the
+     'ifType definitions' registry.
+
+     The latest revision of this YANG module can be obtained from
+     the IANA web site.
+
+     Requests for new values should be made to IANA via
+     email (iana@iana.org).
+
+     Copyright (c) 2014 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     The initial version of this YANG module is part of RFC 7224;
+     see the RFC itself for full legal notices.";
+    reference
+      "IANA 'ifType definitions' registry.
+       <http://www.iana.org/assignments/smi-numbers>";
+
+  revision 2014-05-08 {
+    description
+      "Initial revision.";
+    reference
+      "RFC 7224: IANA Interface Type YANG Module";
+  }
+
+  identity iana-interface-type {
+    base if:interface-type;
+    description
+      "This identity is used as a base for all interface types
+       defined in the 'ifType definitions' registry.";
+  }
+
+
+
+
+
+
+  identity other {
+    base iana-interface-type;
+  }
+  identity regular1822 {
+    base iana-interface-type;
+  }
+  identity hdh1822 {
+    base iana-interface-type;
+  }
+  identity ddnX25 {
+    base iana-interface-type;
+  }
+  identity rfc877x25 {
+    base iana-interface-type;
+    reference
+      "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer";
+  }
+  identity ethernetCsmacd {
+    base iana-interface-type;
+    description
+      "For all Ethernet-like interfaces, regardless of speed,
+       as per RFC 3635.";
+    reference
+      "RFC 3635 - Definitions of Managed Objects for the
+                  Ethernet-like Interface Types";
+  }
+  identity iso88023Csmacd {
+    base iana-interface-type;
+    status deprecated;
+    description
+      "Deprecated via RFC 3635.
+       Use ethernetCsmacd(6) instead.";
+    reference
+      "RFC 3635 - Definitions of Managed Objects for the
+                  Ethernet-like Interface Types";
+  }
+  identity iso88024TokenBus {
+    base iana-interface-type;
+  }
+  identity iso88025TokenRing {
+    base iana-interface-type;
+  }
+  identity iso88026Man {
+    base iana-interface-type;
+  }
+  identity starLan {
+    base iana-interface-type;
+    status deprecated;
+    description
+      "Deprecated via RFC 3635.
+       Use ethernetCsmacd(6) instead.";
+    reference
+      "RFC 3635 - Definitions of Managed Objects for the
+                  Ethernet-like Interface Types";
+  }
+  identity proteon10Mbit {
+    base iana-interface-type;
+  }
+  identity proteon80Mbit {
+    base iana-interface-type;
+  }
+  identity hyperchannel {
+    base iana-interface-type;
+  }
+  identity fddi {
+    base iana-interface-type;
+    reference
+      "RFC 1512 - FDDI Management Information Base";
+  }
+  identity lapb {
+    base iana-interface-type;
+    reference
+      "RFC 1381 - SNMP MIB Extension for X.25 LAPB";
+  }
+  identity sdlc {
+    base iana-interface-type;
+  }
+  identity ds1 {
+    base iana-interface-type;
+    description
+      "DS1-MIB.";
+    reference
+      "RFC 4805 - Definitions of Managed Objects for the
+                  DS1, J1, E1, DS2, and E2 Interface Types";
+  }
+  identity e1 {
+    base iana-interface-type;
+    status obsolete;
+    description
+      "Obsolete; see DS1-MIB.";
+    reference
+      "RFC 4805 - Definitions of Managed Objects for the
+                  DS1, J1, E1, DS2, and E2 Interface Types";
+  }
+
+
+  identity basicISDN {
+    base iana-interface-type;
+    description
+      "No longer used.  See also RFC 2127.";
+  }
+  identity primaryISDN {
+    base iana-interface-type;
+    description
+      "No longer used.  See also RFC 2127.";
+  }
+  identity propPointToPointSerial {
+    base iana-interface-type;
+    description
+      "Proprietary serial.";
+  }
+  identity ppp {
+    base iana-interface-type;
+  }
+  identity softwareLoopback {
+    base iana-interface-type;
+  }
+  identity eon {
+    base iana-interface-type;
+    description
+      "CLNP over IP.";
+  }
+  identity ethernet3Mbit {
+    base iana-interface-type;
+  }
+  identity nsip {
+    base iana-interface-type;
+    description
+      "XNS over IP.";
+  }
+  identity slip {
+    base iana-interface-type;
+    description
+      "Generic SLIP.";
+  }
+  identity ultra {
+    base iana-interface-type;
+    description
+      "Ultra Technologies.";
+  }
+  identity ds3 {
+    base iana-interface-type;
+    description
+      "DS3-MIB.";
+    reference
+      "RFC 3896 - Definitions of Managed Objects for the
+                  DS3/E3 Interface Type";
+  }
+  identity sip {
+    base iana-interface-type;
+    description
+      "SMDS, coffee.";
+    reference
+      "RFC 1694 - Definitions of Managed Objects for SMDS
+                  Interfaces using SMIv2";
+  }
+  identity frameRelay {
+    base iana-interface-type;
+    description
+      "DTE only.";
+    reference
+      "RFC 2115 - Management Information Base for Frame Relay
+                  DTEs Using SMIv2";
+  }
+  identity rs232 {
+    base iana-interface-type;
+    reference
+      "RFC 1659 - Definitions of Managed Objects for RS-232-like
+                  Hardware Devices using SMIv2";
+  }
+  identity para {
+    base iana-interface-type;
+    description
+      "Parallel-port.";
+    reference
+      "RFC 1660 - Definitions of Managed Objects for
+                  Parallel-printer-like Hardware Devices using
+                  SMIv2";
+  }
+  identity arcnet {
+    base iana-interface-type;
+    description
+      "ARCnet.";
+  }
+  identity arcnetPlus {
+    base iana-interface-type;
+    description
+      "ARCnet Plus.";
+  }
+
+
+
+  identity atm {
+    base iana-interface-type;
+    description
+      "ATM cells.";
+  }
+  identity miox25 {
+    base iana-interface-type;
+    reference
+      "RFC 1461 - SNMP MIB extension for Multiprotocol
+                  Interconnect over X.25";
+  }
+  identity sonet {
+    base iana-interface-type;
+    description
+      "SONET or SDH.";
+  }
+  identity x25ple {
+    base iana-interface-type;
+    reference
+      "RFC 2127 - ISDN Management Information Base using SMIv2";
+  }
+  identity iso88022llc {
+    base iana-interface-type;
+  }
+  identity localTalk {
+    base iana-interface-type;
+  }
+  identity smdsDxi {
+    base iana-interface-type;
+  }
+  identity frameRelayService {
+    base iana-interface-type;
+    description
+      "FRNETSERV-MIB.";
+    reference
+      "RFC 2954 - Definitions of Managed Objects for Frame
+                  Relay Service";
+  }
+  identity v35 {
+    base iana-interface-type;
+  }
+  identity hssi {
+    base iana-interface-type;
+  }
+  identity hippi {
+    base iana-interface-type;
+  }
+
+  identity modem {
+    base iana-interface-type;
+    description
+      "Generic modem.";
+  }
+  identity aal5 {
+    base iana-interface-type;
+    description
+      "AAL5 over ATM.";
+  }
+  identity sonetPath {
+    base iana-interface-type;
+  }
+  identity sonetVT {
+    base iana-interface-type;
+  }
+  identity smdsIcip {
+    base iana-interface-type;
+    description
+      "SMDS InterCarrier Interface.";
+  }
+  identity propVirtual {
+    base iana-interface-type;
+    description
+      "Proprietary virtual/internal.";
+    reference
+      "RFC 2863 - The Interfaces Group MIB";
+  }
+  identity propMultiplexor {
+    base iana-interface-type;
+    description
+      "Proprietary multiplexing.";
+    reference
+      "RFC 2863 - The Interfaces Group MIB";
+  }
+  identity ieee80212 {
+    base iana-interface-type;
+    description
+      "100BaseVG.";
+  }
+  identity fibreChannel {
+    base iana-interface-type;
+    description
+      "Fibre Channel.";
+  }
+
+
+
+  identity hippiInterface {
+    base iana-interface-type;
+    description
+      "HIPPI interfaces.";
+  }
+  identity frameRelayInterconnect {
+    base iana-interface-type;
+    status obsolete;
+    description
+      "Obsolete; use either
+       frameRelay(32) or frameRelayService(44).";
+  }
+  identity aflane8023 {
+    base iana-interface-type;
+    description
+      "ATM Emulated LAN for 802.3.";
+  }
+  identity aflane8025 {
+    base iana-interface-type;
+    description
+      "ATM Emulated LAN for 802.5.";
+  }
+  identity cctEmul {
+    base iana-interface-type;
+    description
+      "ATM Emulated circuit.";
+  }
+  identity fastEther {
+    base iana-interface-type;
+    status deprecated;
+    description
+      "Obsoleted via RFC 3635.
+       ethernetCsmacd(6) should be used instead.";
+    reference
+      "RFC 3635 - Definitions of Managed Objects for the
+                  Ethernet-like Interface Types";
+  }
+  identity isdn {
+    base iana-interface-type;
+    description
+      "ISDN and X.25.";
+    reference
+      "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN
+                  in the Packet Mode";
+  }
+
+
+
+  identity v11 {
+    base iana-interface-type;
+    description
+      "CCITT V.11/X.21.";
+  }
+  identity v36 {
+    base iana-interface-type;
+    description
+      "CCITT V.36.";
+  }
+  identity g703at64k {
+    base iana-interface-type;
+    description
+      "CCITT G703 at 64Kbps.";
+  }
+  identity g703at2mb {
+    base iana-interface-type;
+    status obsolete;
+    description
+      "Obsolete; see DS1-MIB.";
+  }
+  identity qllc {
+    base iana-interface-type;
+    description
+      "SNA QLLC.";
+  }
+  identity fastEtherFX {
+    base iana-interface-type;
+    status deprecated;
+    description
+      "Obsoleted via RFC 3635.
+       ethernetCsmacd(6) should be used instead.";
+    reference
+      "RFC 3635 - Definitions of Managed Objects for the
+                  Ethernet-like Interface Types";
+  }
+  identity channel {
+    base iana-interface-type;
+    description
+      "Channel.";
+  }
+  identity ieee80211 {
+    base iana-interface-type;
+    description
+      "Radio spread spectrum.";
+  }
+  identity ibm370parChan {
+    base iana-interface-type;
+    description
+      "IBM System 360/370 OEMI Channel.";
+  }
+  identity escon {
+    base iana-interface-type;
+    description
+      "IBM Enterprise Systems Connection.";
+  }
+  identity dlsw {
+    base iana-interface-type;
+    description
+      "Data Link Switching.";
+  }
+  identity isdns {
+    base iana-interface-type;
+    description
+      "ISDN S/T interface.";
+  }
+  identity isdnu {
+    base iana-interface-type;
+    description
+      "ISDN U interface.";
+  }
+  identity lapd {
+    base iana-interface-type;
+    description
+      "Link Access Protocol D.";
+  }
+  identity ipSwitch {
+    base iana-interface-type;
+    description
+      "IP Switching Objects.";
+  }
+  identity rsrb {
+    base iana-interface-type;
+    description
+      "Remote Source Route Bridging.";
+  }
+  identity atmLogical {
+    base iana-interface-type;
+    description
+      "ATM Logical Port.";
+    reference
+      "RFC 3606 - Definitions of Supplemental Managed Objects
+                  for ATM Interface";
+  }
+  identity ds0 {
+    base iana-interface-type;
+    description
+      "Digital Signal Level 0.";
+    reference
+      "RFC 2494 - Definitions of Managed Objects for the DS0
+                  and DS0 Bundle Interface Type";
+  }
+  identity ds0Bundle {
+    base iana-interface-type;
+    description
+      "Group of ds0s on the same ds1.";
+    reference
+      "RFC 2494 - Definitions of Managed Objects for the DS0
+                  and DS0 Bundle Interface Type";
+  }
+  identity bsc {
+    base iana-interface-type;
+    description
+      "Bisynchronous Protocol.";
+  }
+  identity async {
+    base iana-interface-type;
+    description
+      "Asynchronous Protocol.";
+  }
+  identity cnr {
+    base iana-interface-type;
+    description
+      "Combat Net Radio.";
+  }
+  identity iso88025Dtr {
+    base iana-interface-type;
+    description
+      "ISO 802.5r DTR.";
+  }
+  identity eplrs {
+    base iana-interface-type;
+    description
+      "Ext Pos Loc Report Sys.";
+  }
+  identity arap {
+    base iana-interface-type;
+    description
+      "Appletalk Remote Access Protocol.";
+  }
+  identity propCnls {
+    base iana-interface-type;
+    description
+      "Proprietary Connectionless Protocol.";
+  }
+  identity hostPad {
+    base iana-interface-type;
+    description
+      "CCITT-ITU X.29 PAD Protocol.";
+  }
+  identity termPad {
+    base iana-interface-type;
+    description
+      "CCITT-ITU X.3 PAD Facility.";
+  }
+  identity frameRelayMPI {
+    base iana-interface-type;
+    description
+      "Multiproto Interconnect over FR.";
+  }
+  identity x213 {
+    base iana-interface-type;
+    description
+      "CCITT-ITU X213.";
+  }
+  identity adsl {
+    base iana-interface-type;
+    description
+      "Asymmetric Digital Subscriber Loop.";
+  }
+  identity radsl {
+    base iana-interface-type;
+    description
+      "Rate-Adapt. Digital Subscriber Loop.";
+  }
+  identity sdsl {
+    base iana-interface-type;
+    description
+      "Symmetric Digital Subscriber Loop.";
+  }
+  identity vdsl {
+    base iana-interface-type;
+    description
+      "Very H-Speed Digital Subscrib. Loop.";
+  }
+  identity iso88025CRFPInt {
+    base iana-interface-type;
+    description
+      "ISO 802.5 CRFP.";
+  }
+  identity myrinet {
+    base iana-interface-type;
+    description
+      "Myricom Myrinet.";
+  }
+  identity voiceEM {
+    base iana-interface-type;
+    description
+      "Voice recEive and transMit.";
+  }
+  identity voiceFXO {
+    base iana-interface-type;
+    description
+      "Voice Foreign Exchange Office.";
+  }
+  identity voiceFXS {
+    base iana-interface-type;
+    description
+      "Voice Foreign Exchange Station.";
+  }
+  identity voiceEncap {
+    base iana-interface-type;
+    description
+      "Voice encapsulation.";
+  }
+  identity voiceOverIp {
+    base iana-interface-type;
+    description
+      "Voice over IP encapsulation.";
+  }
+  identity atmDxi {
+    base iana-interface-type;
+    description
+      "ATM DXI.";
+  }
+  identity atmFuni {
+    base iana-interface-type;
+    description
+      "ATM FUNI.";
+  }
+  identity atmIma {
+    base iana-interface-type;
+    description
+      "ATM IMA.";
+  }
+  identity pppMultilinkBundle {
+    base iana-interface-type;
+    description
+      "PPP Multilink Bundle.";
+  }
+  identity ipOverCdlc {
+    base iana-interface-type;
+    description
+      "IBM ipOverCdlc.";
+  }
+  identity ipOverClaw {
+    base iana-interface-type;
+    description
+      "IBM Common Link Access to Workstn.";
+  }
+  identity stackToStack {
+    base iana-interface-type;
+    description
+      "IBM stackToStack.";
+  }
+  identity virtualIpAddress {
+    base iana-interface-type;
+    description
+      "IBM VIPA.";
+  }
+  identity mpc {
+    base iana-interface-type;
+    description
+      "IBM multi-protocol channel support.";
+  }
+  identity ipOverAtm {
+    base iana-interface-type;
+    description
+      "IBM ipOverAtm.";
+    reference
+      "RFC 2320 - Definitions of Managed Objects for Classical IP
+                  and ARP Over ATM Using SMIv2 (IPOA-MIB)";
+  }
+  identity iso88025Fiber {
+    base iana-interface-type;
+    description
+      "ISO 802.5j Fiber Token Ring.";
+  }
+  identity tdlc {
+    base iana-interface-type;
+    description
+      "IBM twinaxial data link control.";
+  }
+  identity gigabitEthernet {
+    base iana-interface-type;
+    status deprecated;
+
+
+    description
+      "Obsoleted via RFC 3635.
+       ethernetCsmacd(6) should be used instead.";
+    reference
+      "RFC 3635 - Definitions of Managed Objects for the
+                  Ethernet-like Interface Types";
+  }
+  identity hdlc {
+    base iana-interface-type;
+    description
+      "HDLC.";
+  }
+  identity lapf {
+    base iana-interface-type;
+    description
+      "LAP F.";
+  }
+  identity v37 {
+    base iana-interface-type;
+    description
+      "V.37.";
+  }
+  identity x25mlp {
+    base iana-interface-type;
+    description
+      "Multi-Link Protocol.";
+  }
+  identity x25huntGroup {
+    base iana-interface-type;
+    description
+      "X25 Hunt Group.";
+  }
+  identity transpHdlc {
+    base iana-interface-type;
+    description
+      "Transp HDLC.";
+  }
+  identity interleave {
+    base iana-interface-type;
+    description
+      "Interleave channel.";
+  }
+  identity fast {
+    base iana-interface-type;
+    description
+      "Fast channel.";
+  }
+
+  identity ip {
+    base iana-interface-type;
+    description
+      "IP (for APPN HPR in IP networks).";
+  }
+  identity docsCableMaclayer {
+    base iana-interface-type;
+    description
+      "CATV Mac Layer.";
+  }
+  identity docsCableDownstream {
+    base iana-interface-type;
+    description
+      "CATV Downstream interface.";
+  }
+  identity docsCableUpstream {
+    base iana-interface-type;
+    description
+      "CATV Upstream interface.";
+  }
+  identity a12MppSwitch {
+    base iana-interface-type;
+    description
+      "Avalon Parallel Processor.";
+  }
+  identity tunnel {
+    base iana-interface-type;
+    description
+      "Encapsulation interface.";
+  }
+  identity coffee {
+    base iana-interface-type;
+    description
+      "Coffee pot.";
+    reference
+      "RFC 2325 - Coffee MIB";
+  }
+  identity ces {
+    base iana-interface-type;
+    description
+      "Circuit Emulation Service.";
+  }
+  identity atmSubInterface {
+    base iana-interface-type;
+    description
+      "ATM Sub Interface.";
+  }
+
+  identity l2vlan {
+    base iana-interface-type;
+    description
+      "Layer 2 Virtual LAN using 802.1Q.";
+  }
+  identity l3ipvlan {
+    base iana-interface-type;
+    description
+      "Layer 3 Virtual LAN using IP.";
+  }
+  identity l3ipxvlan {
+    base iana-interface-type;
+    description
+      "Layer 3 Virtual LAN using IPX.";
+  }
+  identity digitalPowerline {
+    base iana-interface-type;
+    description
+      "IP over Power Lines.";
+  }
+  identity mediaMailOverIp {
+    base iana-interface-type;
+    description
+      "Multimedia Mail over IP.";
+  }
+  identity dtm {
+    base iana-interface-type;
+    description
+      "Dynamic synchronous Transfer Mode.";
+  }
+  identity dcn {
+    base iana-interface-type;
+    description
+      "Data Communications Network.";
+  }
+  identity ipForward {
+    base iana-interface-type;
+    description
+      "IP Forwarding Interface.";
+  }
+  identity msdsl {
+    base iana-interface-type;
+    description
+      "Multi-rate Symmetric DSL.";
+  }
+  identity ieee1394 {
+    base iana-interface-type;
+
+    description
+      "IEEE1394 High Performance Serial Bus.";
+  }
+  identity if-gsn {
+    base iana-interface-type;
+    description
+      "HIPPI-6400.";
+  }
+  identity dvbRccMacLayer {
+    base iana-interface-type;
+    description
+      "DVB-RCC MAC Layer.";
+  }
+  identity dvbRccDownstream {
+    base iana-interface-type;
+    description
+      "DVB-RCC Downstream Channel.";
+  }
+  identity dvbRccUpstream {
+    base iana-interface-type;
+    description
+      "DVB-RCC Upstream Channel.";
+  }
+  identity atmVirtual {
+    base iana-interface-type;
+    description
+      "ATM Virtual Interface.";
+  }
+  identity mplsTunnel {
+    base iana-interface-type;
+    description
+      "MPLS Tunnel Virtual Interface.";
+  }
+  identity srp {
+    base iana-interface-type;
+    description
+      "Spatial Reuse Protocol.";
+  }
+  identity voiceOverAtm {
+    base iana-interface-type;
+    description
+      "Voice over ATM.";
+  }
+  identity voiceOverFrameRelay {
+    base iana-interface-type;
+    description
+      "Voice Over Frame Relay.";
+  }
+  identity idsl {
+    base iana-interface-type;
+    description
+      "Digital Subscriber Loop over ISDN.";
+  }
+  identity compositeLink {
+    base iana-interface-type;
+    description
+      "Avici Composite Link Interface.";
+  }
+  identity ss7SigLink {
+    base iana-interface-type;
+    description
+      "SS7 Signaling Link.";
+  }
+  identity propWirelessP2P {
+    base iana-interface-type;
+    description
+      "Prop. P2P wireless interface.";
+  }
+  identity frForward {
+    base iana-interface-type;
+    description
+      "Frame Forward Interface.";
+  }
+  identity rfc1483 {
+    base iana-interface-type;
+    description
+      "Multiprotocol over ATM AAL5.";
+    reference
+      "RFC 1483 - Multiprotocol Encapsulation over ATM
+                  Adaptation Layer 5";
+  }
+  identity usb {
+    base iana-interface-type;
+    description
+      "USB Interface.";
+  }
+  identity ieee8023adLag {
+    base iana-interface-type;
+    description
+      "IEEE 802.3ad Link Aggregate.";
+  }
+  identity bgppolicyaccounting {
+    base iana-interface-type;
+    description
+      "BGP Policy Accounting.";
+  }
+  identity frf16MfrBundle {
+    base iana-interface-type;
+    description
+      "FRF.16 Multilink Frame Relay.";
+  }
+  identity h323Gatekeeper {
+    base iana-interface-type;
+    description
+      "H323 Gatekeeper.";
+  }
+  identity h323Proxy {
+    base iana-interface-type;
+    description
+      "H323 Voice and Video Proxy.";
+  }
+  identity mpls {
+    base iana-interface-type;
+    description
+      "MPLS.";
+  }
+  identity mfSigLink {
+    base iana-interface-type;
+    description
+      "Multi-frequency signaling link.";
+  }
+  identity hdsl2 {
+    base iana-interface-type;
+    description
+      "High Bit-Rate DSL - 2nd generation.";
+  }
+  identity shdsl {
+    base iana-interface-type;
+    description
+      "Multirate HDSL2.";
+  }
+  identity ds1FDL {
+    base iana-interface-type;
+    description
+      "Facility Data Link (4Kbps) on a DS1.";
+  }
+  identity pos {
+    base iana-interface-type;
+    description
+      "Packet over SONET/SDH Interface.";
+  }
+
+
+
+  identity dvbAsiIn {
+    base iana-interface-type;
+    description
+      "DVB-ASI Input.";
+  }
+  identity dvbAsiOut {
+    base iana-interface-type;
+    description
+      "DVB-ASI Output.";
+  }
+  identity plc {
+    base iana-interface-type;
+    description
+      "Power Line Communications.";
+  }
+  identity nfas {
+    base iana-interface-type;
+    description
+      "Non-Facility Associated Signaling.";
+  }
+  identity tr008 {
+    base iana-interface-type;
+    description
+      "TR008.";
+  }
+  identity gr303RDT {
+    base iana-interface-type;
+    description
+      "Remote Digital Terminal.";
+  }
+  identity gr303IDT {
+    base iana-interface-type;
+    description
+      "Integrated Digital Terminal.";
+  }
+  identity isup {
+    base iana-interface-type;
+    description
+      "ISUP.";
+  }
+  identity propDocsWirelessMaclayer {
+    base iana-interface-type;
+    description
+      "Cisco proprietary Maclayer.";
+  }
+
+
+
+  identity propDocsWirelessDownstream {
+    base iana-interface-type;
+    description
+      "Cisco proprietary Downstream.";
+  }
+  identity propDocsWirelessUpstream {
+    base iana-interface-type;
+    description
+      "Cisco proprietary Upstream.";
+  }
+  identity hiperlan2 {
+    base iana-interface-type;
+    description
+      "HIPERLAN Type 2 Radio Interface.";
+  }
+  identity propBWAp2Mp {
+    base iana-interface-type;
+    description
+      "PropBroadbandWirelessAccesspt2Multipt (use of this value
+       for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f
+       is deprecated, and ieee80216WMAN(237) should be used
+       instead).";
+  }
+  identity sonetOverheadChannel {
+    base iana-interface-type;
+    description
+      "SONET Overhead Channel.";
+  }
+  identity digitalWrapperOverheadChannel {
+    base iana-interface-type;
+    description
+      "Digital Wrapper.";
+  }
+  identity aal2 {
+    base iana-interface-type;
+    description
+      "ATM adaptation layer 2.";
+  }
+  identity radioMAC {
+    base iana-interface-type;
+    description
+      "MAC layer over radio links.";
+  }
+  identity atmRadio {
+    base iana-interface-type;
+    description
+      "ATM over radio links.";
+  }
+  identity imt {
+    base iana-interface-type;
+    description
+      "Inter-Machine Trunks.";
+  }
+  identity mvl {
+    base iana-interface-type;
+    description
+      "Multiple Virtual Lines DSL.";
+  }
+  identity reachDSL {
+    base iana-interface-type;
+    description
+      "Long Reach DSL.";
+  }
+  identity frDlciEndPt {
+    base iana-interface-type;
+    description
+      "Frame Relay DLCI End Point.";
+  }
+  identity atmVciEndPt {
+    base iana-interface-type;
+    description
+      "ATM VCI End Point.";
+  }
+  identity opticalChannel {
+    base iana-interface-type;
+    description
+      "Optical Channel.";
+  }
+  identity opticalTransport {
+    base iana-interface-type;
+    description
+      "Optical Transport.";
+  }
+  identity propAtm {
+    base iana-interface-type;
+    description
+      "Proprietary ATM.";
+  }
+  identity voiceOverCable {
+    base iana-interface-type;
+    description
+      "Voice Over Cable Interface.";
+  }
+
+
+
+  identity infiniband {
+    base iana-interface-type;
+    description
+      "Infiniband.";
+  }
+  identity teLink {
+    base iana-interface-type;
+    description
+      "TE Link.";
+  }
+  identity q2931 {
+    base iana-interface-type;
+    description
+      "Q.2931.";
+  }
+  identity virtualTg {
+    base iana-interface-type;
+    description
+      "Virtual Trunk Group.";
+  }
+  identity sipTg {
+    base iana-interface-type;
+    description
+      "SIP Trunk Group.";
+  }
+  identity sipSig {
+    base iana-interface-type;
+    description
+      "SIP Signaling.";
+  }
+  identity docsCableUpstreamChannel {
+    base iana-interface-type;
+    description
+      "CATV Upstream Channel.";
+  }
+  identity econet {
+    base iana-interface-type;
+    description
+      "Acorn Econet.";
+  }
+  identity pon155 {
+    base iana-interface-type;
+    description
+      "FSAN 155Mb Symetrical PON interface.";
+  }
+
+
+
+  identity pon622 {
+    base iana-interface-type;
+    description
+      "FSAN 622Mb Symetrical PON interface.";
+  }
+  identity bridge {
+    base iana-interface-type;
+    description
+      "Transparent bridge interface.";
+  }
+  identity linegroup {
+    base iana-interface-type;
+    description
+      "Interface common to multiple lines.";
+  }
+  identity voiceEMFGD {
+    base iana-interface-type;
+    description
+      "Voice E&M Feature Group D.";
+  }
+  identity voiceFGDEANA {
+    base iana-interface-type;
+    description
+      "Voice FGD Exchange Access North American.";
+  }
+  identity voiceDID {
+    base iana-interface-type;
+    description
+      "Voice Direct Inward Dialing.";
+  }
+  identity mpegTransport {
+    base iana-interface-type;
+    description
+      "MPEG transport interface.";
+  }
+  identity sixToFour {
+    base iana-interface-type;
+    status deprecated;
+    description
+      "6to4 interface (DEPRECATED).";
+    reference
+      "RFC 4087 - IP Tunnel MIB";
+  }
+  identity gtp {
+    base iana-interface-type;
+    description
+      "GTP (GPRS Tunneling Protocol).";
+  }
+  identity pdnEtherLoop1 {
+    base iana-interface-type;
+    description
+      "Paradyne EtherLoop 1.";
+  }
+  identity pdnEtherLoop2 {
+    base iana-interface-type;
+    description
+      "Paradyne EtherLoop 2.";
+  }
+  identity opticalChannelGroup {
+    base iana-interface-type;
+    description
+      "Optical Channel Group.";
+  }
+  identity homepna {
+    base iana-interface-type;
+    description
+      "HomePNA ITU-T G.989.";
+  }
+  identity gfp {
+    base iana-interface-type;
+    description
+      "Generic Framing Procedure (GFP).";
+  }
+  identity ciscoISLvlan {
+    base iana-interface-type;
+    description
+      "Layer 2 Virtual LAN using Cisco ISL.";
+  }
+  identity actelisMetaLOOP {
+    base iana-interface-type;
+    description
+      "Acteleis proprietary MetaLOOP High Speed Link.";
+  }
+  identity fcipLink {
+    base iana-interface-type;
+    description
+      "FCIP Link.";
+  }
+  identity rpr {
+    base iana-interface-type;
+    description
+      "Resilient Packet Ring Interface Type.";
+  }
+
+
+
+  identity qam {
+    base iana-interface-type;
+    description
+      "RF Qam Interface.";
+  }
+  identity lmp {
+    base iana-interface-type;
+    description
+      "Link Management Protocol.";
+    reference
+      "RFC 4327 - Link Management Protocol (LMP) Management
+                  Information Base (MIB)";
+  }
+  identity cblVectaStar {
+    base iana-interface-type;
+    description
+      "Cambridge Broadband Networks Limited VectaStar.";
+  }
+  identity docsCableMCmtsDownstream {
+    base iana-interface-type;
+    description
+      "CATV Modular CMTS Downstream Interface.";
+  }
+  identity adsl2 {
+    base iana-interface-type;
+    status deprecated;
+    description
+      "Asymmetric Digital Subscriber Loop Version 2
+       (DEPRECATED/OBSOLETED - please use adsl2plus(238)
+       instead).";
+    reference
+      "RFC 4706 - Definitions of Managed Objects for Asymmetric
+                  Digital Subscriber Line 2 (ADSL2)";
+  }
+  identity macSecControlledIF {
+    base iana-interface-type;
+    description
+      "MACSecControlled.";
+  }
+  identity macSecUncontrolledIF {
+    base iana-interface-type;
+    description
+      "MACSecUncontrolled.";
+  }
+  identity aviciOpticalEther {
+    base iana-interface-type;
+    description
+      "Avici Optical Ethernet Aggregate.";
+  }
+  identity atmbond {
+    base iana-interface-type;
+    description
+      "atmbond.";
+  }
+  identity voiceFGDOS {
+    base iana-interface-type;
+    description
+      "Voice FGD Operator Services.";
+  }
+  identity mocaVersion1 {
+    base iana-interface-type;
+    description
+      "MultiMedia over Coax Alliance (MoCA) Interface
+       as documented in information provided privately to IANA.";
+  }
+  identity ieee80216WMAN {
+    base iana-interface-type;
+    description
+      "IEEE 802.16 WMAN interface.";
+  }
+  identity adsl2plus {
+    base iana-interface-type;
+    description
+      "Asymmetric Digital Subscriber Loop Version 2 -
+       Version 2 Plus and all variants.";
+  }
+  identity dvbRcsMacLayer {
+    base iana-interface-type;
+    description
+      "DVB-RCS MAC Layer.";
+    reference
+      "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+  }
+  identity dvbTdm {
+    base iana-interface-type;
+    description
+      "DVB Satellite TDM.";
+    reference
+      "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+  }
+  identity dvbRcsTdma {
+    base iana-interface-type;
+    description
+      "DVB-RCS TDMA.";
+    reference
+      "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+  }
+  identity x86Laps {
+    base iana-interface-type;
+    description
+      "LAPS based on ITU-T X.86/Y.1323.";
+  }
+  identity wwanPP {
+    base iana-interface-type;
+    description
+      "3GPP WWAN.";
+  }
+  identity wwanPP2 {
+    base iana-interface-type;
+    description
+      "3GPP2 WWAN.";
+  }
+  identity voiceEBS {
+    base iana-interface-type;
+    description
+      "Voice P-phone EBS physical interface.";
+  }
+  identity ifPwType {
+    base iana-interface-type;
+    description
+      "Pseudowire interface type.";
+    reference
+      "RFC 5601 - Pseudowire (PW) Management Information Base (MIB)";
+  }
+  identity ilan {
+    base iana-interface-type;
+    description
+      "Internal LAN on a bridge per IEEE 802.1ap.";
+  }
+  identity pip {
+    base iana-interface-type;
+    description
+      "Provider Instance Port on a bridge per IEEE 802.1ah PBB.";
+  }
+  identity aluELP {
+    base iana-interface-type;
+    description
+      "Alcatel-Lucent Ethernet Link Protection.";
+  }
+  identity gpon {
+    base iana-interface-type;
+    description
+      "Gigabit-capable passive optical networks (G-PON) as per
+       ITU-T G.948.";
+  }
+  identity vdsl2 {
+    base iana-interface-type;
+    description
+      "Very high speed digital subscriber line Version 2
+       (as per ITU-T Recommendation G.993.2).";
+    reference
+      "RFC 5650 - Definitions of Managed Objects for Very High
+                  Speed Digital Subscriber Line 2 (VDSL2)";
+  }
+  identity capwapDot11Profile {
+    base iana-interface-type;
+    description
+      "WLAN Profile Interface.";
+    reference
+      "RFC 5834 - Control and Provisioning of Wireless Access
+                  Points (CAPWAP) Protocol Binding MIB for
+                  IEEE 802.11";
+  }
+  identity capwapDot11Bss {
+    base iana-interface-type;
+    description
+      "WLAN BSS Interface.";
+    reference
+      "RFC 5834 - Control and Provisioning of Wireless Access
+                  Points (CAPWAP) Protocol Binding MIB for
+                  IEEE 802.11";
+  }
+  identity capwapWtpVirtualRadio {
+    base iana-interface-type;
+    description
+      "WTP Virtual Radio Interface.";
+    reference
+      "RFC 5833 - Control and Provisioning of Wireless Access
+                  Points (CAPWAP) Protocol Base MIB";
+  }
+  identity bits {
+    base iana-interface-type;
+    description
+      "bitsport.";
+  }
+  identity docsCableUpstreamRfPort {
+    base iana-interface-type;
+    description
+      "DOCSIS CATV Upstream RF Port.";
+  }
+
+
+  identity cableDownstreamRfPort {
+    base iana-interface-type;
+    description
+      "CATV downstream RF Port.";
+  }
+  identity vmwareVirtualNic {
+    base iana-interface-type;
+    description
+      "VMware Virtual Network Interface.";
+  }
+  identity ieee802154 {
+    base iana-interface-type;
+    description
+      "IEEE 802.15.4 WPAN interface.";
+    reference
+      "IEEE 802.15.4-2006";
+  }
+  identity otnOdu {
+    base iana-interface-type;
+    description
+      "OTN Optical Data Unit.";
+  }
+  identity otnOtu {
+    base iana-interface-type;
+    description
+      "OTN Optical channel Transport Unit.";
+  }
+  identity ifVfiType {
+    base iana-interface-type;
+    description
+      "VPLS Forwarding Instance Interface Type.";
+  }
+  identity g9981 {
+    base iana-interface-type;
+    description
+      "G.998.1 bonded interface.";
+  }
+  identity g9982 {
+    base iana-interface-type;
+    description
+      "G.998.2 bonded interface.";
+  }
+  identity g9983 {
+    base iana-interface-type;
+    description
+      "G.998.3 bonded interface.";
+  }
+
+  identity aluEpon {
+    base iana-interface-type;
+    description
+      "Ethernet Passive Optical Networks (E-PON).";
+  }
+  identity aluEponOnu {
+    base iana-interface-type;
+    description
+      "EPON Optical Network Unit.";
+  }
+  identity aluEponPhysicalUni {
+    base iana-interface-type;
+    description
+      "EPON physical User to Network interface.";
+  }
+  identity aluEponLogicalLink {
+    base iana-interface-type;
+    description
+      "The emulation of a point-to-point link over the EPON
+       layer.";
+  }
+  identity aluGponOnu {
+    base iana-interface-type;
+    description
+      "GPON Optical Network Unit.";
+    reference
+      "ITU-T G.984.2";
+  }
+  identity aluGponPhysicalUni {
+    base iana-interface-type;
+    description
+      "GPON physical User to Network interface.";
+    reference
+      "ITU-T G.984.2";
+  }
+  identity vmwareNicTeam {
+    base iana-interface-type;
+    description
+      "VMware NIC Team.";
+  }
+}
diff --git a/tools/lint/examples/ietf-interfaces.yang b/tools/lint/examples/ietf-interfaces.yang
new file mode 100644
index 0000000..ad64425
--- /dev/null
+++ b/tools/lint/examples/ietf-interfaces.yang
@@ -0,0 +1,725 @@
+module ietf-interfaces {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+  prefix if;
+
+  import ietf-yang-types {
+    prefix yang;
+  }
+
+  organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: Thomas Nadeau
+               <mailto:tnadeau@lucidvision.com>
+
+     WG Chair: Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>
+
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>";
+
+  description
+    "This module contains a collection of YANG definitions for
+     managing network interfaces.
+
+     Copyright (c) 2014 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 7223; see
+     the RFC itself for full legal notices.";
+
+  revision 2014-05-08 {
+    description
+      "Initial revision.";
+    reference
+      "RFC 7223: A YANG Data Model for Interface Management";
+  }
+
+  /*
+   * Typedefs
+   */
+
+  typedef interface-ref {
+    type leafref {
+      path "/if:interfaces/if:interface/if:name";
+    }
+    description
+      "This type is used by data models that need to reference
+       configured interfaces.";
+  }
+
+  typedef interface-state-ref {
+    type leafref {
+      path "/if:interfaces-state/if:interface/if:name";
+    }
+    description
+      "This type is used by data models that need to reference
+       the operationally present interfaces.";
+  }
+
+  /*
+   * Identities
+   */
+
+  identity interface-type {
+    description
+      "Base identity from which specific interface types are
+       derived.";
+  }
+
+  /*
+   * Features
+   */
+
+  feature arbitrary-names {
+    description
+      "This feature indicates that the device allows user-controlled
+       interfaces to be named arbitrarily.";
+  }
+  feature pre-provisioning {
+    description
+      "This feature indicates that the device supports
+       pre-provisioning of interface configuration, i.e., it is
+       possible to configure an interface whose physical interface
+       hardware is not present on the device.";
+  }
+
+  feature if-mib {
+    description
+      "This feature indicates that the device implements
+       the IF-MIB.";
+    reference
+      "RFC 2863: The Interfaces Group MIB";
+  }
+
+  /*
+   * Configuration data nodes
+   */
+
+  container interfaces {
+    description
+      "Interface configuration parameters.";
+
+    list interface {
+      key "name";
+
+      description
+        "The list of configured interfaces on the device.
+
+         The operational state of an interface is available in the
+         /interfaces-state/interface list.  If the configuration of a
+         system-controlled interface cannot be used by the system
+         (e.g., the interface hardware present does not match the
+         interface type), then the configuration is not applied to
+         the system-controlled interface shown in the
+         /interfaces-state/interface list.  If the configuration
+         of a user-controlled interface cannot be used by the system,
+         the configured interface is not instantiated in the
+         /interfaces-state/interface list.";
+
+     leaf name {
+        type string;
+        description
+          "The name of the interface.
+
+           A device MAY restrict the allowed values for this leaf,
+           possibly depending on the type of the interface.
+           For system-controlled interfaces, this leaf is the
+           device-specific name of the interface.  The 'config false'
+           list /interfaces-state/interface contains the currently
+           existing interfaces on the device.
+
+           If a client tries to create configuration for a
+           system-controlled interface that is not present in the
+           /interfaces-state/interface list, the server MAY reject
+           the request if the implementation does not support
+           pre-provisioning of interfaces or if the name refers to
+           an interface that can never exist in the system.  A
+           NETCONF server MUST reply with an rpc-error with the
+           error-tag 'invalid-value' in this case.
+
+           If the device supports pre-provisioning of interface
+           configuration, the 'pre-provisioning' feature is
+           advertised.
+
+           If the device allows arbitrarily named user-controlled
+           interfaces, the 'arbitrary-names' feature is advertised.
+
+           When a configured user-controlled interface is created by
+           the system, it is instantiated with the same name in the
+           /interface-state/interface list.";
+      }
+
+      leaf description {
+        type string;
+        description
+          "A textual description of the interface.
+
+           A server implementation MAY map this leaf to the ifAlias
+           MIB object.  Such an implementation needs to use some
+           mechanism to handle the differences in size and characters
+           allowed between this leaf and ifAlias.  The definition of
+           such a mechanism is outside the scope of this document.
+
+           Since ifAlias is defined to be stored in non-volatile
+           storage, the MIB implementation MUST map ifAlias to the
+           value of 'description' in the persistently stored
+           datastore.
+
+           Specifically, if the device supports ':startup', when
+           ifAlias is read the device MUST return the value of
+           'description' in the 'startup' datastore, and when it is
+           written, it MUST be written to the 'running' and 'startup'
+           datastores.  Note that it is up to the implementation to
+
+           decide whether to modify this single leaf in 'startup' or
+           perform an implicit copy-config from 'running' to
+           'startup'.
+
+           If the device does not support ':startup', ifAlias MUST
+           be mapped to the 'description' leaf in the 'running'
+           datastore.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifAlias";
+      }
+
+      leaf type {
+        type identityref {
+          base interface-type;
+        }
+        mandatory true;
+        description
+          "The type of the interface.
+
+           When an interface entry is created, a server MAY
+           initialize the type leaf with a valid value, e.g., if it
+           is possible to derive the type from the name of the
+           interface.
+
+           If a client tries to set the type of an interface to a
+           value that can never be used by the system, e.g., if the
+           type is not supported or if the type does not match the
+           name of the interface, the server MUST reject the request.
+           A NETCONF server MUST reply with an rpc-error with the
+           error-tag 'invalid-value' in this case.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifType";
+      }
+
+      leaf enabled {
+        type boolean;
+        default "true";
+        description
+          "This leaf contains the configured, desired state of the
+           interface.
+
+           Systems that implement the IF-MIB use the value of this
+           leaf in the 'running' datastore to set
+           IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
+           has been initialized, as described in RFC 2863.
+
+
+
+           Changes in this leaf in the 'running' datastore are
+           reflected in ifAdminStatus, but if ifAdminStatus is
+           changed over SNMP, this leaf is not affected.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+      }
+
+      leaf link-up-down-trap-enable {
+        if-feature if-mib;
+        type enumeration {
+          enum enabled {
+            value 1;
+          }
+          enum disabled {
+            value 2;
+          }
+        }
+        description
+          "Controls whether linkUp/linkDown SNMP notifications
+           should be generated for this interface.
+
+           If this node is not configured, the value 'enabled' is
+           operationally used by the server for interfaces that do
+           not operate on top of any other interface (i.e., there are
+           no 'lower-layer-if' entries), and 'disabled' otherwise.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifLinkUpDownTrapEnable";
+      }
+    }
+  }
+
+  /*
+   * Operational state data nodes
+   */
+
+  container interfaces-state {
+    config false;
+    description
+      "Data nodes for the operational state of interfaces.";
+
+    list interface {
+      key "name";
+
+
+
+
+
+      description
+        "The list of interfaces on the device.
+
+         System-controlled interfaces created by the system are
+         always present in this list, whether they are configured or
+         not.";
+
+      leaf name {
+        type string;
+        description
+          "The name of the interface.
+
+           A server implementation MAY map this leaf to the ifName
+           MIB object.  Such an implementation needs to use some
+           mechanism to handle the differences in size and characters
+           allowed between this leaf and ifName.  The definition of
+           such a mechanism is outside the scope of this document.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifName";
+      }
+
+      leaf type {
+        type identityref {
+          base interface-type;
+        }
+        mandatory true;
+        description
+          "The type of the interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifType";
+      }
+
+      leaf admin-status {
+        if-feature if-mib;
+        type enumeration {
+          enum up {
+            value 1;
+            description
+              "Ready to pass packets.";
+          }
+          enum down {
+            value 2;
+            description
+              "Not ready to pass packets and not in some test mode.";
+          }
+
+
+
+          enum testing {
+            value 3;
+            description
+              "In some test mode.";
+          }
+        }
+        mandatory true;
+        description
+          "The desired state of the interface.
+
+           This leaf has the same read semantics as ifAdminStatus.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+      }
+
+      leaf oper-status {
+        type enumeration {
+          enum up {
+            value 1;
+            description
+              "Ready to pass packets.";
+          }
+          enum down {
+            value 2;
+            description
+              "The interface does not pass any packets.";
+          }
+          enum testing {
+            value 3;
+            description
+              "In some test mode.  No operational packets can
+               be passed.";
+          }
+          enum unknown {
+            value 4;
+            description
+              "Status cannot be determined for some reason.";
+          }
+          enum dormant {
+            value 5;
+            description
+              "Waiting for some external event.";
+          }
+          enum not-present {
+            value 6;
+            description
+              "Some component (typically hardware) is missing.";
+          }
+          enum lower-layer-down {
+            value 7;
+            description
+              "Down due to state of lower-layer interface(s).";
+          }
+        }
+        mandatory true;
+        description
+          "The current operational state of the interface.
+
+           This leaf has the same semantics as ifOperStatus.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifOperStatus";
+      }
+
+      leaf last-change {
+        type yang:date-and-time;
+        description
+          "The time the interface entered its current operational
+           state.  If the current state was entered prior to the
+           last re-initialization of the local network management
+           subsystem, then this node is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifLastChange";
+      }
+
+      leaf if-index {
+        if-feature if-mib;
+        type int32 {
+          range "1..2147483647";
+        }
+        mandatory true;
+        description
+          "The ifIndex value for the ifEntry represented by this
+           interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifIndex";
+      }
+
+      leaf phys-address {
+        type yang:phys-address;
+        description
+          "The interface's address at its protocol sub-layer.  For
+           example, for an 802.x interface, this object normally
+           contains a Media Access Control (MAC) address.  The
+           interface's media-specific modules must define the bit
+
+
+           and byte ordering and the format of the value of this
+           object.  For interfaces that do not have such an address
+           (e.g., a serial line), this node is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
+      }
+
+      leaf-list higher-layer-if {
+        type interface-state-ref;
+        description
+          "A list of references to interfaces layered on top of this
+           interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifStackTable";
+      }
+
+      leaf-list lower-layer-if {
+        type interface-state-ref;
+        description
+          "A list of references to interfaces layered underneath this
+           interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifStackTable";
+      }
+
+      leaf speed {
+        type yang:gauge64;
+        units "bits/second";
+        description
+            "An estimate of the interface's current bandwidth in bits
+             per second.  For interfaces that do not vary in
+             bandwidth or for those where no accurate estimation can
+             be made, this node should contain the nominal bandwidth.
+             For interfaces that have no concept of bandwidth, this
+             node is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifSpeed, ifHighSpeed";
+      }
+
+
+
+
+
+
+
+
+
+      container statistics {
+        description
+          "A collection of interface-related statistics objects.";
+
+        leaf discontinuity-time {
+          type yang:date-and-time;
+          mandatory true;
+          description
+            "The time on the most recent occasion at which any one or
+             more of this interface's counters suffered a
+             discontinuity.  If no such discontinuities have occurred
+             since the last re-initialization of the local management
+             subsystem, then this node contains the time the local
+             management subsystem re-initialized itself.";
+        }
+
+        leaf in-octets {
+          type yang:counter64;
+          description
+            "The total number of octets received on the interface,
+             including framing characters.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
+        }
+
+        leaf in-unicast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, that were not addressed to a
+             multicast or broadcast address at this sub-layer.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
+        }
+
+
+
+
+        leaf in-broadcast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, that were addressed to a broadcast
+             address at this sub-layer.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCInBroadcastPkts";
+        }
+
+        leaf in-multicast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, that were addressed to a multicast
+             address at this sub-layer.  For a MAC-layer protocol,
+             this includes both Group and Functional addresses.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCInMulticastPkts";
+        }
+
+        leaf in-discards {
+          type yang:counter32;
+          description
+            "The number of inbound packets that were chosen to be
+             discarded even though no errors had been detected to
+             prevent their being deliverable to a higher-layer
+             protocol.  One possible reason for discarding such a
+             packet could be to free up buffer space.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+
+
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInDiscards";
+        }
+
+        leaf in-errors {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of inbound
+             packets that contained errors preventing them from being
+             deliverable to a higher-layer protocol.  For character-
+             oriented or fixed-length interfaces, the number of
+             inbound transmission units that contained errors
+             preventing them from being deliverable to a higher-layer
+             protocol.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInErrors";
+        }
+
+        leaf in-unknown-protos {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of packets
+             received via the interface that were discarded because
+             of an unknown or unsupported protocol.  For
+             character-oriented or fixed-length interfaces that
+             support protocol multiplexing, the number of
+             transmission units received via the interface that were
+             discarded because of an unknown or unsupported protocol.
+             For any interface that does not support protocol
+             multiplexing, this counter is not present.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
+        }
+
+
+
+
+
+        leaf out-octets {
+          type yang:counter64;
+          description
+            "The total number of octets transmitted out of the
+             interface, including framing characters.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
+        }
+
+        leaf out-unicast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and that were not addressed
+             to a multicast or broadcast address at this sub-layer,
+             including those that were discarded or not sent.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
+        }
+
+        leaf out-broadcast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and that were addressed to a
+             broadcast address at this sub-layer, including those
+             that were discarded or not sent.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCOutBroadcastPkts";
+        }
+
+
+        leaf out-multicast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and that were addressed to a
+             multicast address at this sub-layer, including those
+             that were discarded or not sent.  For a MAC-layer
+             protocol, this includes both Group and Functional
+             addresses.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCOutMulticastPkts";
+        }
+
+        leaf out-discards {
+          type yang:counter32;
+          description
+            "The number of outbound packets that were chosen to be
+             discarded even though no errors had been detected to
+             prevent their being transmitted.  One possible reason
+             for discarding such a packet could be to free up buffer
+             space.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
+        }
+
+        leaf out-errors {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of outbound
+             packets that could not be transmitted because of errors.
+             For character-oriented or fixed-length interfaces, the
+             number of outbound transmission units that could not be
+             transmitted because of errors.
+
+
+
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifOutErrors";
+        }
+      }
+    }
+  }
+}
diff --git a/tools/lint/examples/ietf-ip.yang b/tools/lint/examples/ietf-ip.yang
new file mode 100644
index 0000000..1499120
--- /dev/null
+++ b/tools/lint/examples/ietf-ip.yang
@@ -0,0 +1,758 @@
+module ietf-ip {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
+ prefix ip;
+
+ import ietf-interfaces {
+   prefix if;
+ }
+ import ietf-inet-types {
+   prefix inet;
+ }
+ import ietf-yang-types {
+   prefix yang;
+ }
+
+ organization
+   "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+   "WG Web:   <http://tools.ietf.org/wg/netmod/>
+    WG List:  <mailto:netmod@ietf.org>
+
+    WG Chair: Thomas Nadeau
+              <mailto:tnadeau@lucidvision.com>
+
+    WG Chair: Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>
+
+    Editor:   Martin Bjorklund
+              <mailto:mbj@tail-f.com>";
+
+
+
+
+
+
+
+
+
+
+ description
+   "This module contains a collection of YANG definitions for
+    configuring IP implementations.
+
+    Copyright (c) 2014 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, is permitted pursuant to, and subject
+    to the license terms contained in, the Simplified BSD License
+    set forth in Section 4.c of the IETF Trust's Legal Provisions
+    Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+
+    This version of this YANG module is part of RFC 7277; see
+    the RFC itself for full legal notices.";
+
+ revision 2014-06-16 {
+   description
+     "Initial revision.";
+   reference
+     "RFC 7277: A YANG Data Model for IP Management";
+ }
+
+ /*
+
+  * Features
+  */
+
+ feature ipv4-non-contiguous-netmasks {
+   description
+     "Indicates support for configuring non-contiguous
+      subnet masks.";
+ }
+
+ feature ipv6-privacy-autoconf {
+   description
+     "Indicates support for Privacy Extensions for Stateless Address
+      Autoconfiguration in IPv6.";
+   reference
+     "RFC 4941: Privacy Extensions for Stateless Address
+                Autoconfiguration in IPv6";
+ }
+
+
+
+
+
+ /*
+  * Typedefs
+  */
+
+ typedef ip-address-origin {
+   type enumeration {
+     enum other {
+       description
+         "None of the following.";
+     }
+     enum static {
+       description
+         "Indicates that the address has been statically
+          configured - for example, using NETCONF or a Command Line
+          Interface.";
+     }
+     enum dhcp {
+       description
+         "Indicates an address that has been assigned to this
+          system by a DHCP server.";
+     }
+     enum link-layer {
+       description
+         "Indicates an address created by IPv6 stateless
+          autoconfiguration that embeds a link-layer address in its
+          interface identifier.";
+     }
+     enum random {
+       description
+         "Indicates an address chosen by the system at
+
+          random, e.g., an IPv4 address within 169.254/16, an
+          RFC 4941 temporary address, or an RFC 7217 semantically
+          opaque address.";
+       reference
+         "RFC 4941: Privacy Extensions for Stateless Address
+                    Autoconfiguration in IPv6
+          RFC 7217: A Method for Generating Semantically Opaque
+                    Interface Identifiers with IPv6 Stateless
+                    Address Autoconfiguration (SLAAC)";
+     }
+   }
+   description
+     "The origin of an address.";
+ }
+
+
+
+ typedef neighbor-origin {
+   type enumeration {
+     enum other {
+       description
+         "None of the following.";
+     }
+     enum static {
+       description
+         "Indicates that the mapping has been statically
+          configured - for example, using NETCONF or a Command Line
+          Interface.";
+     }
+     enum dynamic {
+       description
+         "Indicates that the mapping has been dynamically resolved
+          using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery
+          protocol.";
+     }
+   }
+   description
+     "The origin of a neighbor entry.";
+ }
+
+ /*
+  * Configuration data nodes
+  */
+
+ augment "/if:interfaces/if:interface" {
+   description
+     "Parameters for configuring IP on interfaces.
+
+      If an interface is not capable of running IP, the server
+      must not allow the client to configure these parameters.";
+
+   container ipv4 {
+     presence
+       "Enables IPv4 unless the 'enabled' leaf
+        (which defaults to 'true') is set to 'false'";
+     description
+       "Parameters for the IPv4 address family.";
+
+
+
+
+
+
+
+
+     leaf enabled {
+       type boolean;
+       default true;
+       description
+         "Controls whether IPv4 is enabled or disabled on this
+          interface.  When IPv4 is enabled, this interface is
+          connected to an IPv4 stack, and the interface can send
+          and receive IPv4 packets.";
+     }
+     leaf forwarding {
+       type boolean;
+       default false;
+       description
+         "Controls IPv4 packet forwarding of datagrams received by,
+          but not addressed to, this interface.  IPv4 routers
+          forward datagrams.  IPv4 hosts do not (except those
+          source-routed via the host).";
+     }
+     leaf mtu {
+       type uint16 {
+         range "68..max";
+       }
+       units octets;
+       description
+         "The size, in octets, of the largest IPv4 packet that the
+          interface will send and receive.
+
+          The server may restrict the allowed values for this leaf,
+          depending on the interface's type.
+
+          If this leaf is not configured, the operationally used MTU
+          depends on the interface's type.";
+       reference
+         "RFC 791: Internet Protocol";
+     }
+     list address {
+       key "ip";
+       description
+         "The list of configured IPv4 addresses on the interface.";
+
+       leaf ip {
+         type inet:ipv4-address-no-zone;
+         description
+           "The IPv4 address on the interface.";
+       }
+
+
+
+       choice subnet {
+         mandatory true;
+         description
+           "The subnet can be specified as a prefix-length, or,
+            if the server supports non-contiguous netmasks, as
+            a netmask.";
+         leaf prefix-length {
+           type uint8 {
+             range "0..32";
+           }
+           description
+             "The length of the subnet prefix.";
+         }
+         leaf netmask {
+           if-feature ipv4-non-contiguous-netmasks;
+           type yang:dotted-quad;
+           description
+             "The subnet specified as a netmask.";
+         }
+       }
+     }
+     list neighbor {
+       key "ip";
+       description
+         "A list of mappings from IPv4 addresses to
+          link-layer addresses.
+
+          Entries in this list are used as static entries in the
+          ARP Cache.";
+       reference
+         "RFC 826: An Ethernet Address Resolution Protocol";
+
+       leaf ip {
+         type inet:ipv4-address-no-zone;
+         description
+           "The IPv4 address of the neighbor node.";
+       }
+       leaf link-layer-address {
+         type yang:phys-address;
+         mandatory true;
+         description
+           "The link-layer address of the neighbor node.";
+       }
+     }
+
+   }
+
+
+   container ipv6 {
+     presence
+       "Enables IPv6 unless the 'enabled' leaf
+        (which defaults to 'true') is set to 'false'";
+     description
+       "Parameters for the IPv6 address family.";
+
+     leaf enabled {
+       type boolean;
+       default true;
+       description
+         "Controls whether IPv6 is enabled or disabled on this
+          interface.  When IPv6 is enabled, this interface is
+          connected to an IPv6 stack, and the interface can send
+          and receive IPv6 packets.";
+     }
+     leaf forwarding {
+       type boolean;
+       default false;
+       description
+         "Controls IPv6 packet forwarding of datagrams received by,
+          but not addressed to, this interface.  IPv6 routers
+          forward datagrams.  IPv6 hosts do not (except those
+          source-routed via the host).";
+       reference
+         "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+                    Section 6.2.1, IsRouter";
+     }
+     leaf mtu {
+       type uint32 {
+         range "1280..max";
+       }
+       units octets;
+       description
+         "The size, in octets, of the largest IPv6 packet that the
+          interface will send and receive.
+
+          The server may restrict the allowed values for this leaf,
+          depending on the interface's type.
+
+          If this leaf is not configured, the operationally used MTU
+          depends on the interface's type.";
+       reference
+         "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+                    Section 5";
+     }
+
+
+     list address {
+       key "ip";
+       description
+         "The list of configured IPv6 addresses on the interface.";
+
+       leaf ip {
+         type inet:ipv6-address-no-zone;
+         description
+           "The IPv6 address on the interface.";
+       }
+       leaf prefix-length {
+         type uint8 {
+           range "0..128";
+         }
+         mandatory true;
+         description
+           "The length of the subnet prefix.";
+       }
+     }
+     list neighbor {
+       key "ip";
+       description
+         "A list of mappings from IPv6 addresses to
+          link-layer addresses.
+
+          Entries in this list are used as static entries in the
+          Neighbor Cache.";
+       reference
+         "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+       leaf ip {
+         type inet:ipv6-address-no-zone;
+         description
+           "The IPv6 address of the neighbor node.";
+       }
+       leaf link-layer-address {
+         type yang:phys-address;
+         mandatory true;
+         description
+           "The link-layer address of the neighbor node.";
+       }
+     }
+
+
+
+
+
+
+     leaf dup-addr-detect-transmits {
+       type uint32;
+       default 1;
+       description
+         "The number of consecutive Neighbor Solicitation messages
+          sent while performing Duplicate Address Detection on a
+          tentative address.  A value of zero indicates that
+          Duplicate Address Detection is not performed on
+          tentative addresses.  A value of one indicates a single
+          transmission with no follow-up retransmissions.";
+       reference
+         "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+     }
+     container autoconf {
+       description
+         "Parameters to control the autoconfiguration of IPv6
+          addresses, as described in RFC 4862.";
+       reference
+         "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+
+       leaf create-global-addresses {
+         type boolean;
+         default true;
+         description
+           "If enabled, the host creates global addresses as
+            described in RFC 4862.";
+         reference
+           "RFC 4862: IPv6 Stateless Address Autoconfiguration
+                      Section 5.5";
+       }
+       leaf create-temporary-addresses {
+         if-feature ipv6-privacy-autoconf;
+         type boolean;
+         default false;
+         description
+           "If enabled, the host creates temporary addresses as
+            described in RFC 4941.";
+         reference
+           "RFC 4941: Privacy Extensions for Stateless Address
+                      Autoconfiguration in IPv6";
+       }
+
+
+
+
+
+
+
+       leaf temporary-valid-lifetime {
+         if-feature ipv6-privacy-autoconf;
+         type uint32;
+         units "seconds";
+         default 604800;
+         description
+           "The time period during which the temporary address
+            is valid.";
+         reference
+           "RFC 4941: Privacy Extensions for Stateless Address
+                      Autoconfiguration in IPv6
+                      - TEMP_VALID_LIFETIME";
+       }
+       leaf temporary-preferred-lifetime {
+         if-feature ipv6-privacy-autoconf;
+         type uint32;
+         units "seconds";
+         default 86400;
+         description
+           "The time period during which the temporary address is
+            preferred.";
+         reference
+           "RFC 4941: Privacy Extensions for Stateless Address
+                      Autoconfiguration in IPv6
+                      - TEMP_PREFERRED_LIFETIME";
+       }
+     }
+   }
+ }
+
+ /*
+  * Operational state data nodes
+  */
+
+ augment "/if:interfaces-state/if:interface" {
+   description
+     "Data nodes for the operational state of IP on interfaces.";
+
+   container ipv4 {
+     presence "Present if IPv4 is enabled on this interface";
+     config false;
+     description
+       "Interface-specific parameters for the IPv4 address family.";
+
+
+
+
+
+     leaf forwarding {
+       type boolean;
+       description
+         "Indicates whether IPv4 packet forwarding is enabled or
+          disabled on this interface.";
+     }
+     leaf mtu {
+       type uint16 {
+         range "68..max";
+       }
+       units octets;
+       description
+         "The size, in octets, of the largest IPv4 packet that the
+          interface will send and receive.";
+       reference
+         "RFC 791: Internet Protocol";
+     }
+     list address {
+       key "ip";
+       description
+         "The list of IPv4 addresses on the interface.";
+
+       leaf ip {
+         type inet:ipv4-address-no-zone;
+         description
+           "The IPv4 address on the interface.";
+       }
+       choice subnet {
+         description
+           "The subnet can be specified as a prefix-length, or,
+            if the server supports non-contiguous netmasks, as
+            a netmask.";
+         leaf prefix-length {
+           type uint8 {
+             range "0..32";
+           }
+           description
+             "The length of the subnet prefix.";
+         }
+         leaf netmask {
+           if-feature ipv4-non-contiguous-netmasks;
+           type yang:dotted-quad;
+           description
+             "The subnet specified as a netmask.";
+         }
+       }
+
+
+       leaf origin {
+         type ip-address-origin;
+         description
+           "The origin of this address.";
+       }
+     }
+     list neighbor {
+       key "ip";
+       description
+         "A list of mappings from IPv4 addresses to
+          link-layer addresses.
+
+          This list represents the ARP Cache.";
+       reference
+         "RFC 826: An Ethernet Address Resolution Protocol";
+
+       leaf ip {
+         type inet:ipv4-address-no-zone;
+         description
+           "The IPv4 address of the neighbor node.";
+       }
+       leaf link-layer-address {
+         type yang:phys-address;
+         description
+           "The link-layer address of the neighbor node.";
+       }
+       leaf origin {
+         type neighbor-origin;
+         description
+           "The origin of this neighbor entry.";
+       }
+     }
+
+   }
+
+   container ipv6 {
+     presence "Present if IPv6 is enabled on this interface";
+     config false;
+     description
+       "Parameters for the IPv6 address family.";
+
+
+
+
+
+
+
+
+     leaf forwarding {
+       type boolean;
+       default false;
+       description
+         "Indicates whether IPv6 packet forwarding is enabled or
+          disabled on this interface.";
+       reference
+         "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+                    Section 6.2.1, IsRouter";
+     }
+     leaf mtu {
+       type uint32 {
+         range "1280..max";
+       }
+       units octets;
+       description
+         "The size, in octets, of the largest IPv6 packet that the
+          interface will send and receive.";
+       reference
+         "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+                    Section 5";
+     }
+     list address {
+       key "ip";
+       description
+         "The list of IPv6 addresses on the interface.";
+
+       leaf ip {
+         type inet:ipv6-address-no-zone;
+         description
+           "The IPv6 address on the interface.";
+       }
+       leaf prefix-length {
+         type uint8 {
+           range "0..128";
+         }
+         mandatory true;
+         description
+           "The length of the subnet prefix.";
+       }
+       leaf origin {
+         type ip-address-origin;
+         description
+           "The origin of this address.";
+       }
+
+
+
+       leaf status {
+         type enumeration {
+           enum preferred {
+             description
+               "This is a valid address that can appear as the
+                destination or source address of a packet.";
+           }
+           enum deprecated {
+             description
+               "This is a valid but deprecated address that should
+                no longer be used as a source address in new
+                communications, but packets addressed to such an
+                address are processed as expected.";
+           }
+           enum invalid {
+             description
+               "This isn't a valid address, and it shouldn't appear
+                as the destination or source address of a packet.";
+           }
+           enum inaccessible {
+             description
+               "The address is not accessible because the interface
+                to which this address is assigned is not
+                operational.";
+           }
+           enum unknown {
+             description
+               "The status cannot be determined for some reason.";
+           }
+           enum tentative {
+             description
+               "The uniqueness of the address on the link is being
+                verified.  Addresses in this state should not be
+                used for general communication and should only be
+                used to determine the uniqueness of the address.";
+           }
+           enum duplicate {
+             description
+               "The address has been determined to be non-unique on
+                the link and so must not be used.";
+           }
+
+
+
+
+
+
+
+           enum optimistic {
+             description
+               "The address is available for use, subject to
+                restrictions, while its uniqueness on a link is
+                being verified.";
+           }
+         }
+         description
+           "The status of an address.  Most of the states correspond
+            to states from the IPv6 Stateless Address
+            Autoconfiguration protocol.";
+         reference
+           "RFC 4293: Management Information Base for the
+                      Internet Protocol (IP)
+                      - IpAddressStatusTC
+            RFC 4862: IPv6 Stateless Address Autoconfiguration";
+       }
+     }
+     list neighbor {
+       key "ip";
+       description
+         "A list of mappings from IPv6 addresses to
+          link-layer addresses.
+
+          This list represents the Neighbor Cache.";
+       reference
+         "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+       leaf ip {
+         type inet:ipv6-address-no-zone;
+         description
+           "The IPv6 address of the neighbor node.";
+       }
+       leaf link-layer-address {
+         type yang:phys-address;
+         description
+           "The link-layer address of the neighbor node.";
+       }
+       leaf origin {
+         type neighbor-origin;
+         description
+           "The origin of this neighbor entry.";
+       }
+       leaf is-router {
+         type empty;
+         description
+           "Indicates that the neighbor node acts as a router.";
+       }
+       leaf state {
+         type enumeration {
+           enum incomplete {
+             description
+               "Address resolution is in progress, and the link-layer
+                address of the neighbor has not yet been
+                determined.";
+           }
+           enum reachable {
+             description
+               "Roughly speaking, the neighbor is known to have been
+                reachable recently (within tens of seconds ago).";
+           }
+           enum stale {
+             description
+               "The neighbor is no longer known to be reachable, but
+                until traffic is sent to the neighbor no attempt
+                should be made to verify its reachability.";
+           }
+           enum delay {
+             description
+               "The neighbor is no longer known to be reachable, and
+                traffic has recently been sent to the neighbor.
+                Rather than probe the neighbor immediately, however,
+                delay sending probes for a short while in order to
+                give upper-layer protocols a chance to provide
+                reachability confirmation.";
+           }
+           enum probe {
+             description
+               "The neighbor is no longer known to be reachable, and
+                unicast Neighbor Solicitation probes are being sent
+                to verify reachability.";
+           }
+         }
+         description
+           "The Neighbor Unreachability Detection state of this
+            entry.";
+         reference
+           "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+                      Section 7.3.2";
+       }
+     }
+   }
+ }
+}
diff --git a/tools/lint/examples/ietf-netconf-acm-when.yang b/tools/lint/examples/ietf-netconf-acm-when.yang
new file mode 100644
index 0000000..df8f801
--- /dev/null
+++ b/tools/lint/examples/ietf-netconf-acm-when.yang
@@ -0,0 +1,412 @@
+module ietf-netconf-acm-when2 {
+  namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
+  prefix nacm;
+
+  import ietf-yang-types {
+    prefix yang;
+  }
+
+  organization
+    "IETF NETCONF (Network Configuration) Working Group";
+  contact
+    "WG Web:   <http://tools.ietf.org/wg/netconf/>
+     WG List:  <mailto:netconf@ietf.org>
+     
+     WG Chair: Mehmet Ersue
+               <mailto:mehmet.ersue@nsn.com>
+     
+     WG Chair: Bert Wijnen
+               <mailto:bertietf@bwijnen.net>
+     
+     Editor:   Andy Bierman
+               <mailto:andy@yumaworks.com>
+     
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>";
+  description
+    "NETCONF Access Control Model.
+     
+     Copyright (c) 2012 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+     
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD
+     License set forth in Section 4.c of the IETF Trust's
+     Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+     
+     This version of this YANG module is part of RFC 6536; see
+     the RFC itself for full legal notices.";
+
+  revision 2012-02-22 {
+    description
+      "Initial version";
+    reference
+      "RFC 6536: Network Configuration Protocol (NETCONF)
+                 Access Control Model";
+  }
+
+  extension default-deny-write {
+    description
+      "Used to indicate that the data model node
+       represents a sensitive security system parameter.
+       
+       If present, and the NACM module is enabled (i.e.,
+       /nacm/enable-nacm object equals 'true'), the NETCONF server
+       will only allow the designated 'recovery session' to have
+       write access to the node.  An explicit access control rule is
+       required for all other users.
+       
+       The 'default-deny-write' extension MAY appear within a data
+       definition statement.  It is ignored otherwise.";
+  }
+
+  extension default-deny-all {
+    description
+      "Used to indicate that the data model node
+       controls a very sensitive security system parameter.
+       
+       If present, and the NACM module is enabled (i.e.,
+       /nacm/enable-nacm object equals 'true'), the NETCONF server
+       will only allow the designated 'recovery session' to have
+       read, write, or execute access to the node.  An explicit
+       access control rule is required for all other users.
+       
+       The 'default-deny-all' extension MAY appear within a data
+       definition statement, 'rpc' statement, or 'notification'
+       statement.  It is ignored otherwise.";
+  }
+
+  typedef user-name-type {
+    type string {
+      length "1..max";
+    }
+    description
+      "General Purpose Username string.";
+  }
+
+  typedef matchall-string-type {
+    type string {
+      pattern "\\*";
+    }
+    description
+      "The string containing a single asterisk '*' is used
+       to conceptually represent all possible values
+       for the particular leaf using this data type.";
+  }
+
+  typedef access-operations-type {
+    type bits {
+      bit create {
+        description
+          "Any protocol operation that creates a
+           new data node.";
+      }
+      bit read {
+        description
+          "Any protocol operation or notification that
+           returns the value of a data node.";
+      }
+      bit update {
+        description
+          "Any protocol operation that alters an existing
+           data node.";
+      }
+      bit delete {
+        description
+          "Any protocol operation that removes a data node.";
+      }
+      bit exec {
+        description
+          "Execution access to the specified protocol operation.";
+      }
+    }
+    description
+      "NETCONF Access Operation.";
+  }
+
+  typedef group-name-type {
+    type string {
+      length "1..max";
+      pattern "[^\\*].*";
+    }
+    description
+      "Name of administrative group to which
+       users can be assigned.";
+  }
+
+  typedef action-type {
+    type enumeration {
+      enum "permit" {
+        description
+          "Requested action is permitted.";
+      }
+      enum "deny" {
+        description
+          "Requested action is denied.";
+      }
+    }
+    description
+      "Action taken by the server when a particular
+       rule matches.";
+  }
+
+  typedef node-instance-identifier {
+    type yang:xpath1.0;
+    description
+      "Path expression used to represent a special
+       data node instance identifier string.
+       
+       A node-instance-identifier value is an
+       unrestricted YANG instance-identifier expression.
+       All the same rules as an instance-identifier apply
+       except predicates for keys are optional.  If a key
+       predicate is missing, then the node-instance-identifier
+       represents all possible server instances for that key.
+       
+       This XPath expression is evaluated in the following context:
+       
+        o  The set of namespace declarations are those in scope on
+           the leaf element where this type is used.
+       
+        o  The set of variable bindings contains one variable,
+           'USER', which contains the name of the user of the current
+            session.
+       
+        o  The function library is the core function library, but
+           note that due to the syntax restrictions of an
+           instance-identifier, no functions are allowed.
+       
+        o  The context node is the root node in the data tree.";
+  }
+
+  container nacm {
+    nacm:default-deny-all;
+    description
+      "Parameters for NETCONF Access Control Model.";
+    leaf enable-nacm {
+      type boolean;
+      default "true";
+      description
+        "Enables or disables all NETCONF access control
+         enforcement.  If 'true', then enforcement
+         is enabled.  If 'false', then enforcement
+         is disabled.";
+    }
+    leaf read-default {
+      type action-type;
+      default "permit";
+      description
+        "Controls whether read access is granted if
+         no appropriate rule is found for a
+         particular read request.";
+    }
+    leaf write-default {
+      type action-type;
+      default "deny";
+      description
+        "Controls whether create, update, or delete access
+         is granted if no appropriate rule is found for a
+         particular write request.";
+    }
+    leaf exec-default {
+      type action-type;
+      default "permit";
+      description
+        "Controls whether exec access is granted if no appropriate
+         rule is found for a particular protocol operation request.";
+    }
+    leaf enable-external-groups {
+      type boolean;
+      default "true";
+      description
+        "Controls whether the server uses the groups reported by the
+         NETCONF transport layer when it assigns the user to a set of
+         NACM groups.  If this leaf has the value 'false', any group
+         names reported by the transport layer are ignored by the
+         server.";
+    }
+    leaf denied-operations {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      description
+        "Number of times since the server last restarted that a
+         protocol operation request was denied.";
+    }
+    leaf denied-data-writes {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      when "../denied-operations > 0";
+      description
+        "Number of times since the server last restarted that a
+         protocol operation request to alter
+         a configuration datastore was denied.";
+    }
+    leaf denied-notifications {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      description
+        "Number of times since the server last restarted that
+         a notification was dropped for a subscription because
+         access to the event type was denied.";
+    }
+    container groups {
+      description
+        "NETCONF Access Control Groups.";
+      list group {
+        key "name";
+        description
+          "One NACM Group Entry.  This list will only contain
+           configured entries, not any entries learned from
+           any transport protocols.";
+        leaf name {
+          type group-name-type;
+          description
+            "Group name associated with this entry.";
+        }
+        leaf-list user-name {
+          type user-name-type;
+          description
+            "Each entry identifies the username of
+             a member of the group associated with
+             this entry.";
+        }
+      }
+    }
+    list rule-list {
+      key "name";
+      ordered-by user;
+      description
+        "An ordered collection of access control rules.";
+      leaf name {
+        type string {
+          length "1..max";
+        }
+        description
+          "Arbitrary name assigned to the rule-list.";
+      }
+      leaf-list group {
+        type union {
+          type matchall-string-type;
+          type group-name-type;
+        }
+        description
+          "List of administrative groups that will be
+           assigned the associated access rights
+           defined by the 'rule' list.
+           
+           The string '*' indicates that all groups apply to the
+           entry.";
+      }
+      list rule {
+        key "name";
+        ordered-by user;
+        description
+          "One access control rule.
+           
+           Rules are processed in user-defined order until a match is
+           found.  A rule matches if 'module-name', 'rule-type', and
+           'access-operations' match the request.  If a rule
+           matches, the 'action' leaf determines if access is granted
+           or not.";
+        leaf name {
+          type string {
+            length "1..max";
+          }
+          description
+            "Arbitrary name assigned to the rule.";
+        }
+        leaf module-name {
+          type union {
+            type matchall-string-type;
+            type string;
+          }
+          default "*";
+          description
+            "Name of the module associated with this rule.
+             
+             This leaf matches if it has the value '*' or if the
+             object being accessed is defined in the module with the
+             specified module name.";
+        }
+        choice rule-type {
+          description
+            "This choice matches if all leafs present in the rule
+             match the request.  If no leafs are present, the
+             choice matches all requests.";
+          case protocol-operation {
+            leaf rpc-name {
+              type union {
+                type matchall-string-type;
+                type string;
+              }
+              description
+                "This leaf matches if it has the value '*' or if
+                 its value equals the requested protocol operation
+                 name.";
+            }
+          }
+          case notification {
+            leaf notification-name {
+              type union {
+                type matchall-string-type;
+                type string;
+              }
+              description
+                "This leaf matches if it has the value '*' or if its
+                 value equals the requested notification name.";
+            }
+          }
+          case data-node {
+            leaf path {
+              type node-instance-identifier;
+              mandatory true;
+              description
+                "Data Node Instance Identifier associated with the
+                 data node controlled by this rule.
+                 
+                 Configuration data or state data instance
+                 identifiers start with a top-level data node.  A
+                 complete instance identifier is required for this
+                 type of path value.
+                 
+                 The special value '/' refers to all possible
+                 datastore contents.";
+            }
+          }
+        }
+        leaf access-operations {
+          type union {
+            type matchall-string-type;
+            type access-operations-type;
+          }
+          default "*";
+          description
+            "Access operations associated with this rule.
+             
+             This leaf matches if it has the value '*' or if the
+             bit corresponding to the requested operation is set.";
+        }
+        leaf action {
+          type action-type;
+          mandatory true;
+          description
+            "The access control action associated with the
+             rule.  If a rule is determined to match a
+             particular request, then this object is used
+             to determine whether to permit or deny the
+             request.";
+        }
+        leaf comment {
+          type string;
+          description
+            "A textual description of the access rule.";
+        }
+      }
+    }
+  }
+}
diff --git a/tools/lint/examples/ietf-netconf-acm-when.yin b/tools/lint/examples/ietf-netconf-acm-when.yin
new file mode 100644
index 0000000..cbff758
--- /dev/null
+++ b/tools/lint/examples/ietf-netconf-acm-when.yin
@@ -0,0 +1,447 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:nacm="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types" name="ietf-netconf-acm-when">
+  <namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"/>
+  <prefix value="nacm"/>
+  <import module="ietf-yang-types">
+    <prefix value="yang"/>
+  </import>
+  <organization>
+    <text>IETF NETCONF (Network Configuration) Working Group</text>
+  </organization>
+  <contact>
+    <text>WG Web:   &lt;http://tools.ietf.org/wg/netconf/&gt;
+WG List:  &lt;mailto:netconf@ietf.org&gt;
+
+WG Chair: Mehmet Ersue
+          &lt;mailto:mehmet.ersue@nsn.com&gt;
+
+WG Chair: Bert Wijnen
+          &lt;mailto:bertietf@bwijnen.net&gt;
+
+Editor:   Andy Bierman
+          &lt;mailto:andy@yumaworks.com&gt;
+
+Editor:   Martin Bjorklund
+          &lt;mailto:mbj@tail-f.com&gt;</text>
+  </contact>
+  <description>
+    <text>NETCONF Access Control Model.
+
+Copyright (c) 2012 IETF Trust and the persons identified as
+authors of the code.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, is permitted pursuant to, and subject
+to the license terms contained in, the Simplified BSD
+License set forth in Section 4.c of the IETF Trust's
+Legal Provisions Relating to IETF Documents
+(http://trustee.ietf.org/license-info).
+
+This version of this YANG module is part of RFC 6536; see
+the RFC itself for full legal notices.</text>
+  </description>
+  <revision date="2012-02-22">
+    <description>
+      <text>Initial version</text>
+    </description>
+    <reference>
+      <text>RFC 6536: Network Configuration Protocol (NETCONF)
+          Access Control Model</text>
+    </reference>
+  </revision>
+  <extension name="default-deny-write">
+    <description>
+      <text>Used to indicate that the data model node
+represents a sensitive security system parameter.
+
+If present, and the NACM module is enabled (i.e.,
+/nacm/enable-nacm object equals 'true'), the NETCONF server
+will only allow the designated 'recovery session' to have
+write access to the node.  An explicit access control rule is
+required for all other users.
+
+The 'default-deny-write' extension MAY appear within a data
+definition statement.  It is ignored otherwise.</text>
+    </description>
+  </extension>
+  <extension name="default-deny-all">
+    <description>
+      <text>Used to indicate that the data model node
+controls a very sensitive security system parameter.
+
+If present, and the NACM module is enabled (i.e.,
+/nacm/enable-nacm object equals 'true'), the NETCONF server
+will only allow the designated 'recovery session' to have
+read, write, or execute access to the node.  An explicit
+access control rule is required for all other users.
+
+The 'default-deny-all' extension MAY appear within a data
+definition statement, 'rpc' statement, or 'notification'
+statement.  It is ignored otherwise.</text>
+    </description>
+  </extension>
+  <typedef name="user-name-type">
+    <type name="string">
+      <length value="1..max"/>
+    </type>
+    <description>
+      <text>General Purpose Username string.</text>
+    </description>
+  </typedef>
+  <typedef name="matchall-string-type">
+    <type name="string">
+      <pattern value="\*"/>
+    </type>
+    <description>
+      <text>The string containing a single asterisk '*' is used
+to conceptually represent all possible values
+for the particular leaf using this data type.</text>
+    </description>
+  </typedef>
+  <typedef name="access-operations-type">
+    <type name="bits">
+      <bit name="create">
+        <description>
+          <text>Any protocol operation that creates a
+new data node.</text>
+        </description>
+      </bit>
+      <bit name="read">
+        <description>
+          <text>Any protocol operation or notification that
+returns the value of a data node.</text>
+        </description>
+      </bit>
+      <bit name="update">
+        <description>
+          <text>Any protocol operation that alters an existing
+data node.</text>
+        </description>
+      </bit>
+      <bit name="delete">
+        <description>
+          <text>Any protocol operation that removes a data node.</text>
+        </description>
+      </bit>
+      <bit name="exec">
+        <description>
+          <text>Execution access to the specified protocol operation.</text>
+        </description>
+      </bit>
+    </type>
+    <description>
+      <text>NETCONF Access Operation.</text>
+    </description>
+  </typedef>
+  <typedef name="group-name-type">
+    <type name="string">
+      <length value="1..max"/>
+      <pattern value="[^\*].*"/>
+    </type>
+    <description>
+      <text>Name of administrative group to which
+users can be assigned.</text>
+    </description>
+  </typedef>
+  <typedef name="action-type">
+    <type name="enumeration">
+      <enum name="permit">
+        <description>
+          <text>Requested action is permitted.</text>
+        </description>
+      </enum>
+      <enum name="deny">
+        <description>
+          <text>Requested action is denied.</text>
+        </description>
+      </enum>
+    </type>
+    <description>
+      <text>Action taken by the server when a particular
+rule matches.</text>
+    </description>
+  </typedef>
+  <typedef name="node-instance-identifier">
+    <type name="yang:xpath1.0"/>
+    <description>
+      <text>Path expression used to represent a special
+data node instance identifier string.
+
+A node-instance-identifier value is an
+unrestricted YANG instance-identifier expression.
+All the same rules as an instance-identifier apply
+except predicates for keys are optional.  If a key
+predicate is missing, then the node-instance-identifier
+represents all possible server instances for that key.
+
+This XPath expression is evaluated in the following context:
+
+ o  The set of namespace declarations are those in scope on
+    the leaf element where this type is used.
+
+ o  The set of variable bindings contains one variable,
+    'USER', which contains the name of the user of the current
+     session.
+
+ o  The function library is the core function library, but
+    note that due to the syntax restrictions of an
+    instance-identifier, no functions are allowed.
+
+ o  The context node is the root node in the data tree.</text>
+    </description>
+  </typedef>
+  <container name="nacm">
+    <nacm:default-deny-all/>
+    <description>
+      <text>Parameters for NETCONF Access Control Model.</text>
+    </description>
+    <leaf name="enable-nacm">
+      <type name="boolean"/>
+      <default value="true"/>
+      <description>
+        <text>Enables or disables all NETCONF access control
+enforcement.  If 'true', then enforcement
+is enabled.  If 'false', then enforcement
+is disabled.</text>
+      </description>
+    </leaf>
+    <leaf name="read-default">
+      <type name="action-type"/>
+      <default value="permit"/>
+      <description>
+        <text>Controls whether read access is granted if
+no appropriate rule is found for a
+particular read request.</text>
+      </description>
+    </leaf>
+    <leaf name="write-default">
+      <type name="action-type"/>
+      <default value="deny"/>
+      <description>
+        <text>Controls whether create, update, or delete access
+is granted if no appropriate rule is found for a
+particular write request.</text>
+      </description>
+    </leaf>
+    <leaf name="exec-default">
+      <type name="action-type"/>
+      <default value="permit"/>
+      <description>
+        <text>Controls whether exec access is granted if no appropriate
+rule is found for a particular protocol operation request.</text>
+      </description>
+    </leaf>
+    <leaf name="enable-external-groups">
+      <type name="boolean"/>
+      <default value="true"/>
+      <description>
+        <text>Controls whether the server uses the groups reported by the
+NETCONF transport layer when it assigns the user to a set of
+NACM groups.  If this leaf has the value 'false', any group
+names reported by the transport layer are ignored by the
+server.</text>
+      </description>
+    </leaf>
+    <leaf name="denied-operations">
+      <type name="yang:zero-based-counter32"/>
+      <config value="false"/>
+      <mandatory value="true"/>
+      <description>
+        <text>Number of times since the server last restarted that a
+protocol operation request was denied.</text>
+      </description>
+    </leaf>
+    <leaf name="denied-data-writes">
+      <type name="yang:zero-based-counter32"/>
+      <config value="false"/>
+      <mandatory value="true"/>
+      <when value="../denied-operations > 0"/>
+      <description>
+        <text>Number of times since the server last restarted that a
+protocol operation request to alter
+a configuration datastore was denied.</text>
+      </description>
+    </leaf>
+    <leaf name="denied-notifications">
+      <type name="yang:zero-based-counter32"/>
+      <config value="false"/>
+      <mandatory value="true"/>
+      <description>
+        <text>Number of times since the server last restarted that
+a notification was dropped for a subscription because
+access to the event type was denied.</text>
+      </description>
+    </leaf>
+    <container name="groups">
+      <description>
+        <text>NETCONF Access Control Groups.</text>
+      </description>
+      <list name="group">
+        <key value="name"/>
+        <description>
+          <text>One NACM Group Entry.  This list will only contain
+configured entries, not any entries learned from
+any transport protocols.</text>
+        </description>
+        <leaf name="name">
+          <type name="group-name-type"/>
+          <description>
+            <text>Group name associated with this entry.</text>
+          </description>
+        </leaf>
+        <leaf-list name="user-name">
+          <type name="user-name-type"/>
+          <description>
+            <text>Each entry identifies the username of
+a member of the group associated with
+this entry.</text>
+          </description>
+        </leaf-list>
+      </list>
+    </container>
+    <list name="rule-list">
+      <key value="name"/>
+      <ordered-by value="user"/>
+      <description>
+        <text>An ordered collection of access control rules.</text>
+      </description>
+      <leaf name="name">
+        <type name="string">
+          <length value="1..max"/>
+        </type>
+        <description>
+          <text>Arbitrary name assigned to the rule-list.</text>
+        </description>
+      </leaf>
+      <leaf-list name="group">
+        <type name="union">
+          <type name="matchall-string-type"/>
+          <type name="group-name-type"/>
+        </type>
+        <description>
+          <text>List of administrative groups that will be
+assigned the associated access rights
+defined by the 'rule' list.
+
+The string '*' indicates that all groups apply to the
+entry.</text>
+        </description>
+      </leaf-list>
+      <list name="rule">
+        <key value="name"/>
+        <ordered-by value="user"/>
+        <description>
+          <text>One access control rule.
+
+Rules are processed in user-defined order until a match is
+found.  A rule matches if 'module-name', 'rule-type', and
+'access-operations' match the request.  If a rule
+matches, the 'action' leaf determines if access is granted
+or not.</text>
+        </description>
+        <leaf name="name">
+          <type name="string">
+            <length value="1..max"/>
+          </type>
+          <description>
+            <text>Arbitrary name assigned to the rule.</text>
+          </description>
+        </leaf>
+        <leaf name="module-name">
+          <type name="union">
+            <type name="matchall-string-type"/>
+            <type name="string"/>
+          </type>
+          <default value="*"/>
+          <description>
+            <text>Name of the module associated with this rule.
+
+This leaf matches if it has the value '*' or if the
+object being accessed is defined in the module with the
+specified module name.</text>
+          </description>
+        </leaf>
+        <choice name="rule-type">
+          <description>
+            <text>This choice matches if all leafs present in the rule
+match the request.  If no leafs are present, the
+choice matches all requests.</text>
+          </description>
+          <case name="protocol-operation">
+            <leaf name="rpc-name">
+              <type name="union">
+                <type name="matchall-string-type"/>
+                <type name="string"/>
+              </type>
+              <description>
+                <text>This leaf matches if it has the value '*' or if
+its value equals the requested protocol operation
+name.</text>
+              </description>
+            </leaf>
+          </case>
+          <case name="notification">
+            <leaf name="notification-name">
+              <type name="union">
+                <type name="matchall-string-type"/>
+                <type name="string"/>
+              </type>
+              <description>
+                <text>This leaf matches if it has the value '*' or if its
+value equals the requested notification name.</text>
+              </description>
+            </leaf>
+          </case>
+          <case name="data-node">
+            <leaf name="path">
+              <type name="node-instance-identifier"/>
+              <mandatory value="true"/>
+              <description>
+                <text>Data Node Instance Identifier associated with the
+data node controlled by this rule.
+
+Configuration data or state data instance
+identifiers start with a top-level data node.  A
+complete instance identifier is required for this
+type of path value.
+
+The special value '/' refers to all possible
+datastore contents.</text>
+              </description>
+            </leaf>
+          </case>
+        </choice>
+        <leaf name="access-operations">
+          <type name="union">
+            <type name="matchall-string-type"/>
+            <type name="access-operations-type"/>
+          </type>
+          <default value="*"/>
+          <description>
+            <text>Access operations associated with this rule.
+
+This leaf matches if it has the value '*' or if the
+bit corresponding to the requested operation is set.</text>
+          </description>
+        </leaf>
+        <leaf name="action">
+          <type name="action-type"/>
+          <mandatory value="true"/>
+          <description>
+            <text>The access control action associated with the
+rule.  If a rule is determined to match a
+particular request, then this object is used
+to determine whether to permit or deny the
+request.</text>
+          </description>
+        </leaf>
+        <leaf name="comment">
+          <type name="string"/>
+          <description>
+            <text>A textual description of the access rule.</text>
+          </description>
+        </leaf>
+      </list>
+    </list>
+  </container>
+</module>
diff --git a/tools/lint/examples/ietf-netconf-acm-when2.yin b/tools/lint/examples/ietf-netconf-acm-when2.yin
new file mode 100644
index 0000000..f8f25a0
--- /dev/null
+++ b/tools/lint/examples/ietf-netconf-acm-when2.yin
@@ -0,0 +1,447 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:nacm="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types" name="ietf-netconf-acm-when2">
+  <namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"/>
+  <prefix value="nacm"/>
+  <import module="ietf-yang-types">
+    <prefix value="yang"/>
+  </import>
+  <organization>
+    <text>IETF NETCONF (Network Configuration) Working Group</text>
+  </organization>
+  <contact>
+    <text>WG Web:   &lt;http://tools.ietf.org/wg/netconf/&gt;
+WG List:  &lt;mailto:netconf@ietf.org&gt;
+
+WG Chair: Mehmet Ersue
+          &lt;mailto:mehmet.ersue@nsn.com&gt;
+
+WG Chair: Bert Wijnen
+          &lt;mailto:bertietf@bwijnen.net&gt;
+
+Editor:   Andy Bierman
+          &lt;mailto:andy@yumaworks.com&gt;
+
+Editor:   Martin Bjorklund
+          &lt;mailto:mbj@tail-f.com&gt;</text>
+  </contact>
+  <description>
+    <text>NETCONF Access Control Model.
+
+Copyright (c) 2012 IETF Trust and the persons identified as
+authors of the code.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, is permitted pursuant to, and subject
+to the license terms contained in, the Simplified BSD
+License set forth in Section 4.c of the IETF Trust's
+Legal Provisions Relating to IETF Documents
+(http://trustee.ietf.org/license-info).
+
+This version of this YANG module is part of RFC 6536; see
+the RFC itself for full legal notices.</text>
+  </description>
+  <revision date="2012-02-22">
+    <description>
+      <text>Initial version</text>
+    </description>
+    <reference>
+      <text>RFC 6536: Network Configuration Protocol (NETCONF)
+          Access Control Model</text>
+    </reference>
+  </revision>
+  <extension name="default-deny-write">
+    <description>
+      <text>Used to indicate that the data model node
+represents a sensitive security system parameter.
+
+If present, and the NACM module is enabled (i.e.,
+/nacm/enable-nacm object equals 'true'), the NETCONF server
+will only allow the designated 'recovery session' to have
+write access to the node.  An explicit access control rule is
+required for all other users.
+
+The 'default-deny-write' extension MAY appear within a data
+definition statement.  It is ignored otherwise.</text>
+    </description>
+  </extension>
+  <extension name="default-deny-all">
+    <description>
+      <text>Used to indicate that the data model node
+controls a very sensitive security system parameter.
+
+If present, and the NACM module is enabled (i.e.,
+/nacm/enable-nacm object equals 'true'), the NETCONF server
+will only allow the designated 'recovery session' to have
+read, write, or execute access to the node.  An explicit
+access control rule is required for all other users.
+
+The 'default-deny-all' extension MAY appear within a data
+definition statement, 'rpc' statement, or 'notification'
+statement.  It is ignored otherwise.</text>
+    </description>
+  </extension>
+  <typedef name="user-name-type">
+    <type name="string">
+      <length value="1..max"/>
+    </type>
+    <description>
+      <text>General Purpose Username string.</text>
+    </description>
+  </typedef>
+  <typedef name="matchall-string-type">
+    <type name="string">
+      <pattern value="\*"/>
+    </type>
+    <description>
+      <text>The string containing a single asterisk '*' is used
+to conceptually represent all possible values
+for the particular leaf using this data type.</text>
+    </description>
+  </typedef>
+  <typedef name="access-operations-type">
+    <type name="bits">
+      <bit name="create">
+        <description>
+          <text>Any protocol operation that creates a
+new data node.</text>
+        </description>
+      </bit>
+      <bit name="read">
+        <description>
+          <text>Any protocol operation or notification that
+returns the value of a data node.</text>
+        </description>
+      </bit>
+      <bit name="update">
+        <description>
+          <text>Any protocol operation that alters an existing
+data node.</text>
+        </description>
+      </bit>
+      <bit name="delete">
+        <description>
+          <text>Any protocol operation that removes a data node.</text>
+        </description>
+      </bit>
+      <bit name="exec">
+        <description>
+          <text>Execution access to the specified protocol operation.</text>
+        </description>
+      </bit>
+    </type>
+    <description>
+      <text>NETCONF Access Operation.</text>
+    </description>
+  </typedef>
+  <typedef name="group-name-type">
+    <type name="string">
+      <length value="1..max"/>
+      <pattern value="[^\*].*"/>
+    </type>
+    <description>
+      <text>Name of administrative group to which
+users can be assigned.</text>
+    </description>
+  </typedef>
+  <typedef name="action-type">
+    <type name="enumeration">
+      <enum name="permit">
+        <description>
+          <text>Requested action is permitted.</text>
+        </description>
+      </enum>
+      <enum name="deny">
+        <description>
+          <text>Requested action is denied.</text>
+        </description>
+      </enum>
+    </type>
+    <description>
+      <text>Action taken by the server when a particular
+rule matches.</text>
+    </description>
+  </typedef>
+  <typedef name="node-instance-identifier">
+    <type name="yang:xpath1.0"/>
+    <description>
+      <text>Path expression used to represent a special
+data node instance identifier string.
+
+A node-instance-identifier value is an
+unrestricted YANG instance-identifier expression.
+All the same rules as an instance-identifier apply
+except predicates for keys are optional.  If a key
+predicate is missing, then the node-instance-identifier
+represents all possible server instances for that key.
+
+This XPath expression is evaluated in the following context:
+
+ o  The set of namespace declarations are those in scope on
+    the leaf element where this type is used.
+
+ o  The set of variable bindings contains one variable,
+    'USER', which contains the name of the user of the current
+     session.
+
+ o  The function library is the core function library, but
+    note that due to the syntax restrictions of an
+    instance-identifier, no functions are allowed.
+
+ o  The context node is the root node in the data tree.</text>
+    </description>
+  </typedef>
+  <container name="nacm">
+    <nacm:default-deny-all/>
+    <description>
+      <text>Parameters for NETCONF Access Control Model.</text>
+    </description>
+    <leaf name="enable-nacm">
+      <type name="boolean"/>
+      <default value="true"/>
+      <description>
+        <text>Enables or disables all NETCONF access control
+enforcement.  If 'true', then enforcement
+is enabled.  If 'false', then enforcement
+is disabled.</text>
+      </description>
+    </leaf>
+    <leaf name="read-default">
+      <type name="action-type"/>
+      <default value="permit"/>
+      <description>
+        <text>Controls whether read access is granted if
+no appropriate rule is found for a
+particular read request.</text>
+      </description>
+    </leaf>
+    <leaf name="write-default">
+      <type name="action-type"/>
+      <default value="deny"/>
+      <description>
+        <text>Controls whether create, update, or delete access
+is granted if no appropriate rule is found for a
+particular write request.</text>
+      </description>
+    </leaf>
+    <leaf name="exec-default">
+      <type name="action-type"/>
+      <default value="permit"/>
+      <description>
+        <text>Controls whether exec access is granted if no appropriate
+rule is found for a particular protocol operation request.</text>
+      </description>
+    </leaf>
+    <leaf name="enable-external-groups">
+      <type name="boolean"/>
+      <default value="true"/>
+      <description>
+        <text>Controls whether the server uses the groups reported by the
+NETCONF transport layer when it assigns the user to a set of
+NACM groups.  If this leaf has the value 'false', any group
+names reported by the transport layer are ignored by the
+server.</text>
+      </description>
+    </leaf>
+    <leaf name="denied-operations">
+      <type name="yang:zero-based-counter32"/>
+      <config value="false"/>
+      <mandatory value="true"/>
+      <description>
+        <text>Number of times since the server last restarted that a
+protocol operation request was denied.</text>
+      </description>
+    </leaf>
+    <leaf name="denied-data-writes">
+      <type name="yang:zero-based-counter32"/>
+      <config value="false"/>
+      <mandatory value="true"/>
+      <when condition="../denied-operations > 0"/>
+      <description>
+        <text>Number of times since the server last restarted that a
+protocol operation request to alter
+a configuration datastore was denied.</text>
+      </description>
+    </leaf>
+    <leaf name="denied-notifications">
+      <type name="yang:zero-based-counter32"/>
+      <config value="false"/>
+      <mandatory value="true"/>
+      <description>
+        <text>Number of times since the server last restarted that
+a notification was dropped for a subscription because
+access to the event type was denied.</text>
+      </description>
+    </leaf>
+    <container name="groups">
+      <description>
+        <text>NETCONF Access Control Groups.</text>
+      </description>
+      <list name="group">
+        <key value="name"/>
+        <description>
+          <text>One NACM Group Entry.  This list will only contain
+configured entries, not any entries learned from
+any transport protocols.</text>
+        </description>
+        <leaf name="name">
+          <type name="group-name-type"/>
+          <description>
+            <text>Group name associated with this entry.</text>
+          </description>
+        </leaf>
+        <leaf-list name="user-name">
+          <type name="user-name-type"/>
+          <description>
+            <text>Each entry identifies the username of
+a member of the group associated with
+this entry.</text>
+          </description>
+        </leaf-list>
+      </list>
+    </container>
+    <list name="rule-list">
+      <key value="name"/>
+      <ordered-by value="user"/>
+      <description>
+        <text>An ordered collection of access control rules.</text>
+      </description>
+      <leaf name="name">
+        <type name="string">
+          <length value="1..max"/>
+        </type>
+        <description>
+          <text>Arbitrary name assigned to the rule-list.</text>
+        </description>
+      </leaf>
+      <leaf-list name="group">
+        <type name="union">
+          <type name="matchall-string-type"/>
+          <type name="group-name-type"/>
+        </type>
+        <description>
+          <text>List of administrative groups that will be
+assigned the associated access rights
+defined by the 'rule' list.
+
+The string '*' indicates that all groups apply to the
+entry.</text>
+        </description>
+      </leaf-list>
+      <list name="rule">
+        <key value="name"/>
+        <ordered-by value="user"/>
+        <description>
+          <text>One access control rule.
+
+Rules are processed in user-defined order until a match is
+found.  A rule matches if 'module-name', 'rule-type', and
+'access-operations' match the request.  If a rule
+matches, the 'action' leaf determines if access is granted
+or not.</text>
+        </description>
+        <leaf name="name">
+          <type name="string">
+            <length value="1..max"/>
+          </type>
+          <description>
+            <text>Arbitrary name assigned to the rule.</text>
+          </description>
+        </leaf>
+        <leaf name="module-name">
+          <type name="union">
+            <type name="matchall-string-type"/>
+            <type name="string"/>
+          </type>
+          <default value="*"/>
+          <description>
+            <text>Name of the module associated with this rule.
+
+This leaf matches if it has the value '*' or if the
+object being accessed is defined in the module with the
+specified module name.</text>
+          </description>
+        </leaf>
+        <choice name="rule-type">
+          <description>
+            <text>This choice matches if all leafs present in the rule
+match the request.  If no leafs are present, the
+choice matches all requests.</text>
+          </description>
+          <case name="protocol-operation">
+            <leaf name="rpc-name">
+              <type name="union">
+                <type name="matchall-string-type"/>
+                <type name="string"/>
+              </type>
+              <description>
+                <text>This leaf matches if it has the value '*' or if
+its value equals the requested protocol operation
+name.</text>
+              </description>
+            </leaf>
+          </case>
+          <case name="notification">
+            <leaf name="notification-name">
+              <type name="union">
+                <type name="matchall-string-type"/>
+                <type name="string"/>
+              </type>
+              <description>
+                <text>This leaf matches if it has the value '*' or if its
+value equals the requested notification name.</text>
+              </description>
+            </leaf>
+          </case>
+          <case name="data-node">
+            <leaf name="path">
+              <type name="node-instance-identifier"/>
+              <mandatory value="true"/>
+              <description>
+                <text>Data Node Instance Identifier associated with the
+data node controlled by this rule.
+
+Configuration data or state data instance
+identifiers start with a top-level data node.  A
+complete instance identifier is required for this
+type of path value.
+
+The special value '/' refers to all possible
+datastore contents.</text>
+              </description>
+            </leaf>
+          </case>
+        </choice>
+        <leaf name="access-operations">
+          <type name="union">
+            <type name="matchall-string-type"/>
+            <type name="access-operations-type"/>
+          </type>
+          <default value="*"/>
+          <description>
+            <text>Access operations associated with this rule.
+
+This leaf matches if it has the value '*' or if the
+bit corresponding to the requested operation is set.</text>
+          </description>
+        </leaf>
+        <leaf name="action">
+          <type name="action-type"/>
+          <mandatory value="true"/>
+          <description>
+            <text>The access control action associated with the
+rule.  If a rule is determined to match a
+particular request, then this object is used
+to determine whether to permit or deny the
+request.</text>
+          </description>
+        </leaf>
+        <leaf name="comment">
+          <type name="string"/>
+          <description>
+            <text>A textual description of the access rule.</text>
+          </description>
+        </leaf>
+      </list>
+    </list>
+  </container>
+</module>
diff --git a/tools/lint/examples/ietf-netconf-acm.yang b/tools/lint/examples/ietf-netconf-acm.yang
new file mode 100644
index 0000000..dc3655e
--- /dev/null
+++ b/tools/lint/examples/ietf-netconf-acm.yang
@@ -0,0 +1,411 @@
+module ietf-netconf-acm {
+  namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
+  prefix nacm;
+
+  import ietf-yang-types {
+    prefix yang;
+  }
+
+  organization
+    "IETF NETCONF (Network Configuration) Working Group";
+  contact
+    "WG Web:   <http://tools.ietf.org/wg/netconf/>
+     WG List:  <mailto:netconf@ietf.org>
+     
+     WG Chair: Mehmet Ersue
+               <mailto:mehmet.ersue@nsn.com>
+     
+     WG Chair: Bert Wijnen
+               <mailto:bertietf@bwijnen.net>
+     
+     Editor:   Andy Bierman
+               <mailto:andy@yumaworks.com>
+     
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>";
+  description
+    "NETCONF Access Control Model.
+     
+     Copyright (c) 2012 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+     
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD
+     License set forth in Section 4.c of the IETF Trust's
+     Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+     
+     This version of this YANG module is part of RFC 6536; see
+     the RFC itself for full legal notices.";
+
+  revision 2012-02-22 {
+    description
+      "Initial version";
+    reference
+      "RFC 6536: Network Configuration Protocol (NETCONF)
+                 Access Control Model";
+  }
+
+  extension default-deny-write {
+    description
+      "Used to indicate that the data model node
+       represents a sensitive security system parameter.
+       
+       If present, and the NACM module is enabled (i.e.,
+       /nacm/enable-nacm object equals 'true'), the NETCONF server
+       will only allow the designated 'recovery session' to have
+       write access to the node.  An explicit access control rule is
+       required for all other users.
+       
+       The 'default-deny-write' extension MAY appear within a data
+       definition statement.  It is ignored otherwise.";
+  }
+
+  extension default-deny-all {
+    description
+      "Used to indicate that the data model node
+       controls a very sensitive security system parameter.
+       
+       If present, and the NACM module is enabled (i.e.,
+       /nacm/enable-nacm object equals 'true'), the NETCONF server
+       will only allow the designated 'recovery session' to have
+       read, write, or execute access to the node.  An explicit
+       access control rule is required for all other users.
+       
+       The 'default-deny-all' extension MAY appear within a data
+       definition statement, 'rpc' statement, or 'notification'
+       statement.  It is ignored otherwise.";
+  }
+
+  typedef user-name-type {
+    type string {
+      length "1..max";
+    }
+    description
+      "General Purpose Username string.";
+  }
+
+  typedef matchall-string-type {
+    type string {
+      pattern "\\*";
+    }
+    description
+      "The string containing a single asterisk '*' is used
+       to conceptually represent all possible values
+       for the particular leaf using this data type.";
+  }
+
+  typedef access-operations-type {
+    type bits {
+      bit create {
+        description
+          "Any protocol operation that creates a
+           new data node.";
+      }
+      bit read {
+        description
+          "Any protocol operation or notification that
+           returns the value of a data node.";
+      }
+      bit update {
+        description
+          "Any protocol operation that alters an existing
+           data node.";
+      }
+      bit delete {
+        description
+          "Any protocol operation that removes a data node.";
+      }
+      bit exec {
+        description
+          "Execution access to the specified protocol operation.";
+      }
+    }
+    description
+      "NETCONF Access Operation.";
+  }
+
+  typedef group-name-type {
+    type string {
+      length "1..max";
+      pattern "[^\\*].*";
+    }
+    description
+      "Name of administrative group to which
+       users can be assigned.";
+  }
+
+  typedef action-type {
+    type enumeration {
+      enum "permit" {
+        description
+          "Requested action is permitted.";
+      }
+      enum "deny" {
+        description
+          "Requested action is denied.";
+      }
+    }
+    description
+      "Action taken by the server when a particular
+       rule matches.";
+  }
+
+  typedef node-instance-identifier {
+    type yang:xpath1.0;
+    description
+      "Path expression used to represent a special
+       data node instance identifier string.
+       
+       A node-instance-identifier value is an
+       unrestricted YANG instance-identifier expression.
+       All the same rules as an instance-identifier apply
+       except predicates for keys are optional.  If a key
+       predicate is missing, then the node-instance-identifier
+       represents all possible server instances for that key.
+       
+       This XPath expression is evaluated in the following context:
+       
+        o  The set of namespace declarations are those in scope on
+           the leaf element where this type is used.
+       
+        o  The set of variable bindings contains one variable,
+           'USER', which contains the name of the user of the current
+            session.
+       
+        o  The function library is the core function library, but
+           note that due to the syntax restrictions of an
+           instance-identifier, no functions are allowed.
+       
+        o  The context node is the root node in the data tree.";
+  }
+
+  container nacm {
+    nacm:default-deny-all;
+    description
+      "Parameters for NETCONF Access Control Model.";
+    leaf enable-nacm {
+      type boolean;
+      default "true";
+      description
+        "Enables or disables all NETCONF access control
+         enforcement.  If 'true', then enforcement
+         is enabled.  If 'false', then enforcement
+         is disabled.";
+    }
+    leaf read-default {
+      type action-type;
+      default "permit";
+      description
+        "Controls whether read access is granted if
+         no appropriate rule is found for a
+         particular read request.";
+    }
+    leaf write-default {
+      type action-type;
+      default "deny";
+      description
+        "Controls whether create, update, or delete access
+         is granted if no appropriate rule is found for a
+         particular write request.";
+    }
+    leaf exec-default {
+      type action-type;
+      default "permit";
+      description
+        "Controls whether exec access is granted if no appropriate
+         rule is found for a particular protocol operation request.";
+    }
+    leaf enable-external-groups {
+      type boolean;
+      default "true";
+      description
+        "Controls whether the server uses the groups reported by the
+         NETCONF transport layer when it assigns the user to a set of
+         NACM groups.  If this leaf has the value 'false', any group
+         names reported by the transport layer are ignored by the
+         server.";
+    }
+    leaf denied-operations {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      description
+        "Number of times since the server last restarted that a
+         protocol operation request was denied.";
+    }
+    leaf denied-data-writes {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      description
+        "Number of times since the server last restarted that a
+         protocol operation request to alter
+         a configuration datastore was denied.";
+    }
+    leaf denied-notifications {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      description
+        "Number of times since the server last restarted that
+         a notification was dropped for a subscription because
+         access to the event type was denied.";
+    }
+    container groups {
+      description
+        "NETCONF Access Control Groups.";
+      list group {
+        key "name";
+        description
+          "One NACM Group Entry.  This list will only contain
+           configured entries, not any entries learned from
+           any transport protocols.";
+        leaf name {
+          type group-name-type;
+          description
+            "Group name associated with this entry.";
+        }
+        leaf-list user-name {
+          type user-name-type;
+          description
+            "Each entry identifies the username of
+             a member of the group associated with
+             this entry.";
+        }
+      }
+    }
+    list rule-list {
+      key "name";
+      ordered-by user;
+      description
+        "An ordered collection of access control rules.";
+      leaf name {
+        type string {
+          length "1..max";
+        }
+        description
+          "Arbitrary name assigned to the rule-list.";
+      }
+      leaf-list group {
+        type union {
+          type matchall-string-type;
+          type group-name-type;
+        }
+        description
+          "List of administrative groups that will be
+           assigned the associated access rights
+           defined by the 'rule' list.
+           
+           The string '*' indicates that all groups apply to the
+           entry.";
+      }
+      list rule {
+        key "name";
+        ordered-by user;
+        description
+          "One access control rule.
+           
+           Rules are processed in user-defined order until a match is
+           found.  A rule matches if 'module-name', 'rule-type', and
+           'access-operations' match the request.  If a rule
+           matches, the 'action' leaf determines if access is granted
+           or not.";
+        leaf name {
+          type string {
+            length "1..max";
+          }
+          description
+            "Arbitrary name assigned to the rule.";
+        }
+        leaf module-name {
+          type union {
+            type matchall-string-type;
+            type string;
+          }
+          default "*";
+          description
+            "Name of the module associated with this rule.
+             
+             This leaf matches if it has the value '*' or if the
+             object being accessed is defined in the module with the
+             specified module name.";
+        }
+        choice rule-type {
+          description
+            "This choice matches if all leafs present in the rule
+             match the request.  If no leafs are present, the
+             choice matches all requests.";
+          case protocol-operation {
+            leaf rpc-name {
+              type union {
+                type matchall-string-type;
+                type string;
+              }
+              description
+                "This leaf matches if it has the value '*' or if
+                 its value equals the requested protocol operation
+                 name.";
+            }
+          }
+          case notification {
+            leaf notification-name {
+              type union {
+                type matchall-string-type;
+                type string;
+              }
+              description
+                "This leaf matches if it has the value '*' or if its
+                 value equals the requested notification name.";
+            }
+          }
+          case data-node {
+            leaf path {
+              type node-instance-identifier;
+              mandatory true;
+              description
+                "Data Node Instance Identifier associated with the
+                 data node controlled by this rule.
+                 
+                 Configuration data or state data instance
+                 identifiers start with a top-level data node.  A
+                 complete instance identifier is required for this
+                 type of path value.
+                 
+                 The special value '/' refers to all possible
+                 datastore contents.";
+            }
+          }
+        }
+        leaf access-operations {
+          type union {
+            type matchall-string-type;
+            type access-operations-type;
+          }
+          default "*";
+          description
+            "Access operations associated with this rule.
+             
+             This leaf matches if it has the value '*' or if the
+             bit corresponding to the requested operation is set.";
+        }
+        leaf action {
+          type action-type;
+          mandatory true;
+          description
+            "The access control action associated with the
+             rule.  If a rule is determined to match a
+             particular request, then this object is used
+             to determine whether to permit or deny the
+             request.";
+        }
+        leaf comment {
+          type string;
+          description
+            "A textual description of the access rule.";
+        }
+      }
+    }
+  }
+}
diff --git a/tools/lint/examples/module1.yang b/tools/lint/examples/module1.yang
new file mode 100644
index 0000000..1df7bf1
--- /dev/null
+++ b/tools/lint/examples/module1.yang
@@ -0,0 +1,5 @@
+module module1 {
+  namespace "urn:yanglint:module";
+  prefix m;
+  leaf m { type string; }
+}
diff --git a/tools/lint/examples/module1b.yang b/tools/lint/examples/module1b.yang
new file mode 100644
index 0000000..463c936
--- /dev/null
+++ b/tools/lint/examples/module1b.yang
@@ -0,0 +1,5 @@
+module module1b {
+  namespace "urn:yanglint:module";
+  prefix m;
+  leaf mb { type string; }
+}
diff --git a/tools/lint/examples/module2.yang b/tools/lint/examples/module2.yang
new file mode 100644
index 0000000..c87c764
--- /dev/null
+++ b/tools/lint/examples/module2.yang
@@ -0,0 +1,5 @@
+module module2 {
+  namespace "urn:yanglint:module";
+  prefix m;
+  leaf m { ttype string; }
+}
diff --git a/tools/lint/examples/module2.yin b/tools/lint/examples/module2.yin
new file mode 100644
index 0000000..af6cb50
--- /dev/null
+++ b/tools/lint/examples/module2.yin
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module name="module2"
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+        xmlns:m="urn:yanglint:module">
+  <namespace uri="urn:yanglint:module"/>
+  <prefix value="m"/>
+  <leaf name="m">
+    <type value="string"/>
+  </leaf>
+</module>
diff --git a/tools/lint/examples/module3.yang b/tools/lint/examples/module3.yang
new file mode 100644
index 0000000..63754b1
--- /dev/null
+++ b/tools/lint/examples/module3.yang
@@ -0,0 +1,8 @@
+module module3 {
+  namespace "urn:yanglint:module";
+  prefix m;
+  leaf m { type string; must "../c/a"; }
+  container c {
+    leaf b { type string; }
+  }
+}
diff --git a/tools/lint/examples/module4.yang b/tools/lint/examples/module4.yang
new file mode 100644
index 0000000..23ea289
--- /dev/null
+++ b/tools/lint/examples/module4.yang
@@ -0,0 +1,52 @@
+module module4 {
+  yang-version 1.1;
+  namespace "urn:module4";
+  prefix m4;
+
+  container cont1 {
+    list list {
+      key "leaf1";
+      leaf leaf1 {
+        type string;
+      }
+      action act {
+        input {
+          leaf leaf2 {
+            type string;
+          }
+        }
+        output {
+          leaf leaf3 {
+            type string;
+          }
+        }
+      }
+      notification notif1 {
+        leaf leaf4 {
+          type string;
+        }
+      }
+    }
+  }
+
+  rpc rpc {
+    input {
+      leaf leaf5 {
+        type string;
+      }
+    }
+    output {
+      container cont2 {
+        leaf leaf6 {
+          type empty;
+        }
+      }
+    }
+  }
+
+  notification notif2 {
+    leaf leaf7 {
+      type empty;
+    }
+  }
+}
diff --git a/tools/lint/examples/nested-notification.xml b/tools/lint/examples/nested-notification.xml
new file mode 100644
index 0000000..024b65a
--- /dev/null
+++ b/tools/lint/examples/nested-notification.xml
@@ -0,0 +1,8 @@
+<cont1 xmlns="urn:module4">
+  <list>
+    <leaf1>key_val</leaf1>
+    <notif1>
+      <leaf4>some_value</leaf4>
+    </notif1>
+  </list>
+</cont1>
diff --git a/tools/lint/examples/notification.xml b/tools/lint/examples/notification.xml
new file mode 100644
index 0000000..803ddad
--- /dev/null
+++ b/tools/lint/examples/notification.xml
@@ -0,0 +1,3 @@
+<notif2 xmlns="urn:module4">
+  <leaf7/>
+</notif2>
diff --git a/tools/lint/examples/rpc-reply.xml b/tools/lint/examples/rpc-reply.xml
new file mode 100644
index 0000000..20469dd
--- /dev/null
+++ b/tools/lint/examples/rpc-reply.xml
@@ -0,0 +1,3 @@
+<cont2 xmlns="urn:module4">
+  <leaf6/>
+</cont2>
diff --git a/tools/lint/examples/rpc.xml b/tools/lint/examples/rpc.xml
new file mode 100644
index 0000000..ea8ca90
--- /dev/null
+++ b/tools/lint/examples/rpc.xml
@@ -0,0 +1,3 @@
+<rpc xmlns="urn:module4">
+  <leaf5>some_input</leaf5>
+</rpc>
diff --git a/tools/lint/linenoise/LICENSE b/tools/lint/linenoise/LICENSE
new file mode 100644
index 0000000..18e8148
--- /dev/null
+++ b/tools/lint/linenoise/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tools/lint/linenoise/linenoise.c b/tools/lint/linenoise/linenoise.c
new file mode 100644
index 0000000..135a712
--- /dev/null
+++ b/tools/lint/linenoise/linenoise.c
@@ -0,0 +1,1227 @@
+/* linenoise.c -- VERSION 1.0
+ *
+ * Guerrilla line editing library against the idea that a line editing lib
+ * needs to be 20,000 lines of C code.
+ *
+ * You can find the latest source code at:
+ *
+ *   http://github.com/antirez/linenoise
+ *
+ * Does a number of crazy assumptions that happen to be true in 99.9999% of
+ * the 2010 UNIX computers around.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  *  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *  *  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * References:
+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+ *
+ * Todo list:
+ * - Filter bogus Ctrl+<char> combinations.
+ * - Win32 support
+ *
+ * Bloat:
+ * - History search like Ctrl+r in readline?
+ *
+ * List of escape sequences used by this program, we do everything just
+ * with three sequences. In order to be so cheap we may have some
+ * flickering effect with some slow terminal, but the lesser sequences
+ * the more compatible.
+ *
+ * EL (Erase Line)
+ *    Sequence: ESC [ n K
+ *    Effect: if n is 0 or missing, clear from cursor to end of line
+ *    Effect: if n is 1, clear from beginning of line to cursor
+ *    Effect: if n is 2, clear entire line
+ *
+ * CUF (CUrsor Forward)
+ *    Sequence: ESC [ n C
+ *    Effect: moves cursor forward n chars
+ *
+ * CUB (CUrsor Backward)
+ *    Sequence: ESC [ n D
+ *    Effect: moves cursor backward n chars
+ *
+ * The following is used to get the terminal width if getting
+ * the width with the TIOCGWINSZ ioctl fails
+ *
+ * DSR (Device Status Report)
+ *    Sequence: ESC [ 6 n
+ *    Effect: reports the current cusor position as ESC [ n ; m R
+ *            where n is the row and m is the column
+ *
+ * When multi line mode is enabled, we also use an additional escape
+ * sequence. However multi line editing is disabled by default.
+ *
+ * CUU (Cursor Up)
+ *    Sequence: ESC [ n A
+ *    Effect: moves cursor up of n chars.
+ *
+ * CUD (Cursor Down)
+ *    Sequence: ESC [ n B
+ *    Effect: moves cursor down of n chars.
+ *
+ * When linenoiseClearScreen() is called, two additional escape sequences
+ * are used in order to clear the screen and position the cursor at home
+ * position.
+ *
+ * CUP (Cursor position)
+ *    Sequence: ESC [ H
+ *    Effect: moves the cursor to upper left corner
+ *
+ * ED (Erase display)
+ *    Sequence: ESC [ 2 J
+ *    Effect: clear the whole screen
+ *
+ */
+
+#define _GNU_SOURCE
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "linenoise.h"
+
+#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
+#define LINENOISE_MAX_LINE 4096
+static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
+static linenoiseCompletionCallback *completionCallback = NULL;
+
+static struct termios orig_termios; /* In order to restore at exit.*/
+static int mlmode = 0;  /* Multi line mode. Default is single line. */
+static int atexit_registered = 0; /* Register atexit just 1 time. */
+static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
+static int history_len = 0;
+static char **history = NULL;
+
+/* The linenoiseState structure represents the state during line editing.
+ * We pass this state to functions implementing specific editing
+ * functionalities. */
+struct linenoiseState ls;
+
+enum KEY_ACTION{
+	KEY_NULL = 0,	    /* NULL */
+	CTRL_A = 1,         /* Ctrl+a */
+	CTRL_B = 2,         /* Ctrl-b */
+	CTRL_C = 3,         /* Ctrl-c */
+	CTRL_D = 4,         /* Ctrl-d */
+	CTRL_E = 5,         /* Ctrl-e */
+	CTRL_F = 6,         /* Ctrl-f */
+	CTRL_H = 8,         /* Ctrl-h */
+	TAB = 9,            /* Tab */
+	CTRL_K = 11,        /* Ctrl+k */
+	CTRL_L = 12,        /* Ctrl+l */
+	ENTER = 13,         /* Enter */
+	CTRL_N = 14,        /* Ctrl-n */
+	CTRL_P = 16,        /* Ctrl-p */
+	CTRL_T = 20,        /* Ctrl-t */
+	CTRL_U = 21,        /* Ctrl+u */
+	CTRL_W = 23,        /* Ctrl+w */
+	ESC = 27,           /* Escape */
+	BACKSPACE =  127    /* Backspace */
+};
+
+static void linenoiseAtExit(void);
+int linenoiseHistoryAdd(const char *line);
+
+/* Debugging macro. */
+#if 0
+FILE *lndebug_fp = NULL;
+#define lndebug(...) \
+    do { \
+        if (lndebug_fp == NULL) { \
+            lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
+            fprintf(lndebug_fp, \
+            "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
+            (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
+            (int)l->maxrows,old_rows); \
+        } \
+        fprintf(lndebug_fp, ", " __VA_ARGS__); \
+        fflush(lndebug_fp); \
+    } while (0)
+#else
+#define lndebug(fmt, ...)
+#endif
+
+/* ======================= Low level terminal handling ====================== */
+
+/* Set if to use or not the multi line mode. */
+void linenoiseSetMultiLine(int ml) {
+    mlmode = ml;
+}
+
+/* Return true if the terminal name is in the list of terminals we know are
+ * not able to understand basic escape sequences. */
+static int isUnsupportedTerm(void) {
+    char *term = getenv("TERM");
+    int j;
+
+    if (term == NULL) return 0;
+    for (j = 0; unsupported_term[j]; j++)
+        if (!strcasecmp(term,unsupported_term[j])) return 1;
+    return 0;
+}
+
+/* Raw mode: 1960 magic shit. */
+int linenoiseEnableRawMode(int fd) {
+    struct termios raw;
+
+    if (!isatty(STDIN_FILENO)) goto fatal;
+    if (!atexit_registered) {
+        atexit(linenoiseAtExit);
+        atexit_registered = 1;
+    }
+    if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
+
+    raw = orig_termios;  /* modify the original mode */
+    /* input modes: no break, no CR to NL, no parity check, no strip char,
+     * no start/stop output control. */
+    raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+    /* output modes - disable post processing */
+    raw.c_oflag &= ~(OPOST);
+    /* control modes - set 8 bit chars */
+    raw.c_cflag |= (CS8);
+    /* local modes - choing off, canonical off, no extended functions,
+     * no signal chars (^Z,^C) */
+    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+    /* control chars - set return condition: min number of bytes and timer.
+     * We want read to return every single byte, without timeout. */
+    raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+
+    /* put terminal in raw mode after flushing */
+    if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
+    ls.rawmode = 1;
+    return 0;
+
+fatal:
+    errno = ENOTTY;
+    return -1;
+}
+
+void linenoiseDisableRawMode(int fd) {
+    /* Don't even check the return value as it's too late. */
+    if (ls.rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
+        ls.rawmode = 0;
+}
+
+/* Use the ESC [6n escape sequence to query the horizontal cursor position
+ * and return it. On error -1 is returned, on success the position of the
+ * cursor. */
+static int getCursorPosition(int ifd, int ofd) {
+    char buf[32];
+    int cols, rows;
+    unsigned int i = 0;
+
+    /* Report cursor location */
+    if (write(ofd, "\x1b[6n", 4) != 4) return -1;
+
+    /* Read the response: ESC [ rows ; cols R */
+    while (i < sizeof(buf)-1) {
+        if (read(ifd,buf+i,1) != 1) break;
+        if (buf[i] == 'R') break;
+        i++;
+    }
+    buf[i] = '\0';
+
+    /* Parse it. */
+    if (buf[0] != ESC || buf[1] != '[') return -1;
+    if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
+    return cols;
+}
+
+/* Try to get the number of columns in the current terminal, or assume 80
+ * if it fails. */
+static int getColumns(int ifd, int ofd) {
+    struct winsize ws;
+
+    if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+        /* ioctl() failed. Try to query the terminal itself. */
+        int start, cols;
+
+        /* Get the initial position so we can restore it later. */
+        start = getCursorPosition(ifd,ofd);
+        if (start == -1) goto failed;
+
+        /* Go to right margin and get position. */
+        if (write(ofd,"\x1b[999C",6) != 6) goto failed;
+        cols = getCursorPosition(ifd,ofd);
+        if (cols == -1) goto failed;
+
+        /* Restore position. */
+        if (cols > start) {
+            char seq[32];
+            snprintf(seq,32,"\x1b[%dD",cols-start);
+            if (write(ofd,seq,strlen(seq)) == -1) {
+                /* Can't recover... */
+            }
+        }
+        return cols;
+    } else {
+        return ws.ws_col;
+    }
+
+failed:
+    return 80;
+}
+
+/* Clear the screen. Used to handle ctrl+l */
+void linenoiseClearScreen(void) {
+    if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
+        /* nothing to do, just to avoid warning. */
+    }
+}
+
+/* Beep, used for completion when there is nothing to complete or when all
+ * the choices were already shown. */
+static void linenoiseBeep(void) {
+    fprintf(stderr, "\x7");
+    fflush(stderr);
+}
+
+/* ============================== Completion ================================ */
+
+/* Free a list of completion option populated by linenoiseAddCompletion(). */
+static void freeCompletions(linenoiseCompletions *lc) {
+    size_t i;
+    for (i = 0; i < lc->len; i++)
+        free(lc->cvec[i]);
+    if (lc->cvec != NULL)
+        free(lc->cvec);
+}
+
+/* This is an helper function for linenoiseEdit() and is called when the
+ * user types the <tab> key in order to complete the string currently in the
+ * input.
+ *
+ * The state of the editing is encapsulated into the pointed linenoiseState
+ * structure as described in the structure definition. */
+static char completeLine(struct linenoiseState *ls) {
+    linenoiseCompletions lc = {0, 0, NULL};
+    int nread, nwritten, hint_len, hint_line_count, char_count;
+    char c = 0, *common, *hint;
+    struct winsize w;
+
+    /* Hint is only the string after the last space */
+    hint = strrchr(ls->buf, ' ');
+    if (!hint) {
+        hint = ls->buf;
+    } else {
+        ++hint;
+    }
+
+    completionCallback(ls->buf, hint, &lc);
+    if (lc.len == 0) {
+        linenoiseBeep();
+    } else {
+        unsigned int i, j;
+
+        /* Learn the longest common part */
+        common = strdup(lc.cvec[0]);
+        for (i = 1; i < lc.len; ++i) {
+            for (j = 0; j < strlen(lc.cvec[i]); ++j) {
+                if (lc.cvec[i][j] != common[j]) {
+                    break;
+                }
+            }
+            common[j] = '\0';
+        }
+
+        /* Path completions have a different hint */
+        if (lc.path && strrchr(hint, '/')) {
+            hint = strrchr(hint, '/');
+            ++hint;
+        }
+
+        /* Show completion */
+        if ((lc.len == 1) && (common[strlen(common) - 1] != '/')) {
+            nwritten = snprintf(hint, ls->buflen - (hint - ls->buf), "%s ", common);
+        } else {
+            nwritten = snprintf(hint, ls->buflen - (hint - ls->buf), "%s", common);
+        }
+        free(common);
+        ls->len = ls->pos = (hint - ls->buf) + nwritten;
+        linenoiseRefreshLine();
+
+        /* A single hint */
+        if (lc.len == 1) {
+            freeCompletions(&lc);
+            return 0;
+        }
+
+        /* Read a char */
+        nread = read(ls->ifd,&c,1);
+        if (nread <= 0) {
+            freeCompletions(&lc);
+            return -1;
+        }
+
+        /* Not a tab */
+        if (c != 9) {
+            freeCompletions(&lc);
+            return c;
+        }
+
+        /* Learn terminal window size */
+        ioctl(ls->ifd, TIOCGWINSZ, &w);
+
+        /* Learn the longest hint */
+        hint_len = strlen(lc.cvec[0]);
+        for (i = 1; i < lc.len; ++i) {
+            if (strlen(lc.cvec[i]) > (unsigned)hint_len) {
+                hint_len = strlen(lc.cvec[i]);
+            }
+        }
+
+        /* Learn the number of hints that fit a line */
+        hint_line_count = 0;
+        while (1) {
+            char_count = 0;
+            if (hint_line_count) {
+                char_count += hint_line_count * (hint_len + 2);
+            }
+            char_count += hint_len;
+
+            /* Too much */
+            if (char_count > w.ws_col) {
+                break;
+            }
+
+            /* Still fits */
+            ++hint_line_count;
+        }
+
+        /* No hint fits, too bad */
+        if (!hint_line_count) {
+            freeCompletions(&lc);
+            return c;
+        }
+
+        while (c == 9) {
+            /* Second tab */
+            linenoiseDisableRawMode(ls->ifd);
+            printf("\n");
+            for (i = 0; i < lc.len; ++i) {
+                printf("%-*s", hint_len, lc.cvec[i]);
+                /* Line full or last hint */
+                if (((i + 1) % hint_line_count == 0) || (i == lc.len - 1)) {
+                    printf("\n");
+                } else {
+                    printf("  ");
+                }
+            }
+            linenoiseEnableRawMode(ls->ifd);
+            linenoiseRefreshLine();
+
+            /* Read a char */
+            nread = read(ls->ifd,&c,1);
+            if (nread <= 0) {
+                freeCompletions(&lc);
+                return -1;
+            }
+        }
+    }
+
+    freeCompletions(&lc);
+    return c; /* Return last read character */
+}
+
+/* Register a callback function to be called for tab-completion. */
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
+    completionCallback = fn;
+}
+
+/* This function can be called in user completion callback to fill
+ * path completion for them. hint parameter is actually the whole path
+ * and buf is unused, but included to match the completion callback prototype. */
+void linenoisePathCompletion(const char *buf, const char *hint, linenoiseCompletions *lc) {
+    const char *ptr;
+    char *full_path, *hint_ptr, match[FILENAME_MAX + 2];
+    DIR *dir;
+    struct dirent *ent;
+    struct stat st;
+
+    (void)buf;
+
+    lc->path = 1;
+
+    ptr = strrchr(hint, '/');
+
+    /* new relative path */
+    if (ptr == NULL) {
+        full_path = malloc(2 + FILENAME_MAX + 1);
+        strcpy(full_path, "./");
+
+        ptr = hint;
+    } else {
+        full_path = malloc((int)(ptr - hint) + FILENAME_MAX + 1);
+        ++ptr;
+        sprintf(full_path, "%.*s", (int)(ptr - hint), hint);
+    }
+    hint_ptr = full_path + strlen(full_path);
+
+    dir = opendir(full_path);
+    if (dir == NULL) {
+        free(full_path);
+        return;
+    }
+
+    while ((ent = readdir(dir))) {
+        if (ent->d_name[0] == '.') {
+            continue;
+        }
+
+        if (!strncmp(ptr, ent->d_name, strlen(ptr))) {
+            /* is it a directory? */
+            strcpy(hint_ptr, ent->d_name);
+            if (stat(full_path, &st)) {
+                /* skip this item */
+                continue;
+            }
+
+            strcpy(match, ent->d_name);
+            if (S_ISDIR(st.st_mode)) {
+                strcat(match, "/");
+            }
+
+            linenoiseAddCompletion(lc, match);
+        }
+    }
+
+    free(full_path);
+    closedir(dir);
+}
+
+/* This function is used by the callback function registered by the user
+ * in order to add completion options given the input string when the
+ * user typed <tab>. See the example.c source code for a very easy to
+ * understand example. */
+void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
+    size_t len = strlen(str);
+    char *copy, **cvec;
+
+    copy = malloc(len+1);
+    if (copy == NULL) return;
+    memcpy(copy,str,len+1);
+    cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
+    if (cvec == NULL) {
+        free(copy);
+        return;
+    }
+    lc->cvec = cvec;
+    lc->cvec[lc->len++] = copy;
+}
+
+/* =========================== Line editing ================================= */
+
+/* We define a very simple "append buffer" structure, that is an heap
+ * allocated string where we can append to. This is useful in order to
+ * write all the escape sequences in a buffer and flush them to the standard
+ * output in a single call, to avoid flickering effects. */
+struct abuf {
+    char *b;
+    int len;
+};
+
+static void abInit(struct abuf *ab) {
+    ab->b = NULL;
+    ab->len = 0;
+}
+
+static void abAppend(struct abuf *ab, const char *s, int len) {
+    char *new = realloc(ab->b,ab->len+len);
+
+    if (new == NULL) return;
+    memcpy(new+ab->len,s,len);
+    ab->b = new;
+    ab->len += len;
+}
+
+static void abFree(struct abuf *ab) {
+    free(ab->b);
+}
+
+/* Single line low level line refresh.
+ *
+ * Rewrite the currently edited line accordingly to the buffer content,
+ * cursor position, and number of columns of the terminal. */
+static void refreshSingleLine(struct linenoiseState *l) {
+    char seq[64];
+    size_t plen = strlen(l->prompt);
+    int fd = l->ofd;
+    char *buf = l->buf;
+    size_t len = l->len;
+    size_t pos = l->pos;
+    struct abuf ab;
+
+    while((plen+pos) >= l->cols) {
+        buf++;
+        len--;
+        pos--;
+    }
+    while (plen+len > l->cols) {
+        len--;
+    }
+
+    abInit(&ab);
+    /* Cursor to left edge */
+    snprintf(seq,64,"\r");
+    abAppend(&ab,seq,strlen(seq));
+    /* Write the prompt and the current buffer content */
+    abAppend(&ab,l->prompt,strlen(l->prompt));
+    abAppend(&ab,buf,len);
+    /* Erase to right */
+    snprintf(seq,64,"\x1b[0K");
+    abAppend(&ab,seq,strlen(seq));
+    /* Move cursor to original position. */
+    snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
+    abAppend(&ab,seq,strlen(seq));
+    if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+    abFree(&ab);
+}
+
+/* Multi line low level line refresh.
+ *
+ * Rewrite the currently edited line accordingly to the buffer content,
+ * cursor position, and number of columns of the terminal. */
+static void refreshMultiLine(struct linenoiseState *l) {
+    char seq[64];
+    int plen = strlen(l->prompt);
+    int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */
+    int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */
+    int rpos2; /* rpos after refresh. */
+    int col; /* colum position, zero-based. */
+    int old_rows = l->maxrows;
+    int fd = l->ofd, j;
+    struct abuf ab;
+
+    /* Update maxrows if needed. */
+    if (rows > (int)l->maxrows) l->maxrows = rows;
+
+    /* First step: clear all the lines used before. To do so start by
+     * going to the last row. */
+    abInit(&ab);
+    if (old_rows-rpos > 0) {
+        lndebug("go down %d", old_rows-rpos);
+        snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
+        abAppend(&ab,seq,strlen(seq));
+    }
+
+    /* Now for every row clear it, go up. */
+    for (j = 0; j < old_rows-1; j++) {
+        lndebug("clear+up");
+        snprintf(seq,64,"\r\x1b[0K\x1b[1A");
+        abAppend(&ab,seq,strlen(seq));
+    }
+
+    /* Clean the top line. */
+    lndebug("clear");
+    snprintf(seq,64,"\r\x1b[0K");
+    abAppend(&ab,seq,strlen(seq));
+
+    /* Write the prompt and the current buffer content */
+    abAppend(&ab,l->prompt,strlen(l->prompt));
+    abAppend(&ab,l->buf,l->len);
+
+    /* If we are at the very end of the screen with our prompt, we need to
+     * emit a newline and move the prompt to the first column. */
+    if (l->pos &&
+        l->pos == l->len &&
+        (l->pos+plen) % l->cols == 0)
+    {
+        lndebug("<newline>");
+        abAppend(&ab,"\n",1);
+        snprintf(seq,64,"\r");
+        abAppend(&ab,seq,strlen(seq));
+        rows++;
+        if (rows > (int)l->maxrows) l->maxrows = rows;
+    }
+
+    /* Move cursor to right position. */
+    rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
+    lndebug("rpos2 %d", rpos2);
+
+    /* Go up till we reach the expected positon. */
+    if (rows-rpos2 > 0) {
+        lndebug("go-up %d", rows-rpos2);
+        snprintf(seq,64,"\x1b[%dA", rows-rpos2);
+        abAppend(&ab,seq,strlen(seq));
+    }
+
+    /* Set column. */
+    col = (plen+(int)l->pos) % (int)l->cols;
+    lndebug("set col %d", 1+col);
+    if (col)
+        snprintf(seq,64,"\r\x1b[%dC", col);
+    else
+        snprintf(seq,64,"\r");
+    abAppend(&ab,seq,strlen(seq));
+
+    lndebug("\n");
+    l->oldpos = l->pos;
+
+    if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+    abFree(&ab);
+}
+
+/* Calls the two low level functions refreshSingleLine() or
+ * refreshMultiLine() according to the selected mode. */
+void linenoiseRefreshLine(void) {
+    if (mlmode)
+        refreshMultiLine(&ls);
+    else
+        refreshSingleLine(&ls);
+}
+
+/* Insert the character 'c' at cursor current position.
+ *
+ * On error writing to the terminal -1 is returned, otherwise 0. */
+int linenoiseEditInsert(struct linenoiseState *l, char c) {
+    if (l->len < l->buflen) {
+        if (l->len == l->pos) {
+            l->buf[l->pos] = c;
+            l->pos++;
+            l->len++;
+            l->buf[l->len] = '\0';
+            if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) {
+                /* Avoid a full update of the line in the
+                 * trivial case. */
+                if (write(l->ofd,&c,1) == -1) return -1;
+            } else {
+                linenoiseRefreshLine();
+            }
+        } else {
+            memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);
+            l->buf[l->pos] = c;
+            l->len++;
+            l->pos++;
+            l->buf[l->len] = '\0';
+            linenoiseRefreshLine();
+        }
+    }
+    return 0;
+}
+
+/* Move cursor on the left. */
+void linenoiseEditMoveLeft(struct linenoiseState *l) {
+    if (l->pos > 0) {
+        l->pos--;
+        linenoiseRefreshLine();
+    }
+}
+
+/* Move cursor on the right. */
+void linenoiseEditMoveRight(struct linenoiseState *l) {
+    if (l->pos != l->len) {
+        l->pos++;
+        linenoiseRefreshLine();
+    }
+}
+
+/* Move cursor to the start of the line. */
+void linenoiseEditMoveHome(struct linenoiseState *l) {
+    if (l->pos != 0) {
+        l->pos = 0;
+        linenoiseRefreshLine();
+    }
+}
+
+/* Move cursor to the end of the line. */
+void linenoiseEditMoveEnd(struct linenoiseState *l) {
+    if (l->pos != l->len) {
+        l->pos = l->len;
+        linenoiseRefreshLine();
+    }
+}
+
+/* Substitute the currently edited line with the next or previous history
+ * entry as specified by 'dir'. */
+#define LINENOISE_HISTORY_NEXT 0
+#define LINENOISE_HISTORY_PREV 1
+void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {
+    if (history_len > 1) {
+        /* Update the current history entry before to
+         * overwrite it with the next one. */
+        free(history[history_len - 1 - l->history_index]);
+        history[history_len - 1 - l->history_index] = strdup(l->buf);
+        /* Show the new entry */
+        l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
+        if (l->history_index < 0) {
+            l->history_index = 0;
+            return;
+        } else if (l->history_index >= history_len) {
+            l->history_index = history_len-1;
+            return;
+        }
+        strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);
+        l->buf[l->buflen-1] = '\0';
+        l->len = l->pos = strlen(l->buf);
+        linenoiseRefreshLine();
+    }
+}
+
+/* Delete the character at the right of the cursor without altering the cursor
+ * position. Basically this is what happens with the "Delete" keyboard key. */
+void linenoiseEditDelete(struct linenoiseState *l) {
+    if (l->len > 0 && l->pos < l->len) {
+        memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1);
+        l->len--;
+        l->buf[l->len] = '\0';
+        linenoiseRefreshLine();
+    }
+}
+
+/* Backspace implementation. */
+void linenoiseEditBackspace(struct linenoiseState *l) {
+    if (l->pos > 0 && l->len > 0) {
+        memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos);
+        l->pos--;
+        l->len--;
+        l->buf[l->len] = '\0';
+        linenoiseRefreshLine();
+    }
+}
+
+/* Delete the previosu word, maintaining the cursor at the start of the
+ * current word. */
+void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
+    size_t old_pos = l->pos;
+    size_t diff;
+
+    while (l->pos > 0 && l->buf[l->pos-1] == ' ')
+        l->pos--;
+    while (l->pos > 0 && l->buf[l->pos-1] != ' ')
+        l->pos--;
+    diff = old_pos - l->pos;
+    memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);
+    l->len -= diff;
+    linenoiseRefreshLine();
+}
+
+/* This function is the core of the line editing capability of linenoise.
+ * It expects 'fd' to be already in "raw mode" so that every key pressed
+ * will be returned ASAP to read().
+ *
+ * The resulting string is put into 'buf' when the user type enter, or
+ * when ctrl+d is typed.
+ *
+ * The function returns the length of the current buffer. */
+static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
+{
+    /* Populate the linenoise state that we pass to functions implementing
+     * specific editing functionalities. */
+    ls.ifd = stdin_fd;
+    ls.ofd = stdout_fd;
+    ls.buf = buf;
+    ls.buflen = buflen;
+    ls.prompt = prompt;
+    ls.plen = strlen(prompt);
+    ls.oldpos = ls.pos = 0;
+    ls.len = 0;
+    ls.cols = getColumns(stdin_fd, stdout_fd);
+    ls.maxrows = 0;
+    ls.history_index = 0;
+
+    /* Buffer starts empty. */
+    ls.buf[0] = '\0';
+    ls.buflen--; /* Make sure there is always space for the nulterm */
+
+    /* The latest history entry is always our current buffer, that
+     * initially is just an empty string. */
+    linenoiseHistoryAdd("");
+
+    if (write(ls.ofd,prompt,ls.plen) == -1) return -1;
+    while(1) {
+        char c = 0;
+        int nread;
+        char seq[3];
+
+        nread = read(ls.ifd,&c,sizeof c);
+        if (nread <= 0) return ls.len;
+
+        /* Only autocomplete when the callback is set. It returns < 0 when
+         * there was an error reading from fd. Otherwise it will return the
+         * character that should be handled next. */
+        if (c == 9 && completionCallback != NULL) {
+            c = completeLine(&ls);
+            /* Return on errors */
+            if (c < 0) return ls.len;
+            /* Read next character when 0 */
+            if (c == 0) continue;
+        }
+
+        switch(c) {
+        case ENTER:    /* enter */
+            history_len--;
+            free(history[history_len]);
+            if (mlmode) linenoiseEditMoveEnd(&ls);
+            return (int)ls.len;
+        case CTRL_C:     /* ctrl-c */
+            errno = EAGAIN;
+            return -1;
+        case BACKSPACE:   /* backspace */
+        case 8:     /* ctrl-h */
+            linenoiseEditBackspace(&ls);
+            break;
+        case CTRL_D:     /* ctrl-d, remove char at right of cursor, or if the
+                            line is empty, act as end-of-file. */
+            if (ls.len > 0) {
+                linenoiseEditDelete(&ls);
+            } else {
+                history_len--;
+                free(history[history_len]);
+                return -1;
+            }
+            break;
+        case CTRL_T:    /* ctrl-t, swaps current character with previous. */
+            if (ls.pos > 0 && ls.pos < ls.len) {
+                int aux = buf[ls.pos-1];
+                buf[ls.pos-1] = buf[ls.pos];
+                buf[ls.pos] = aux;
+                if (ls.pos != ls.len-1) ls.pos++;
+                linenoiseRefreshLine();
+            }
+            break;
+        case CTRL_B:     /* ctrl-b */
+            linenoiseEditMoveLeft(&ls);
+            break;
+        case CTRL_F:     /* ctrl-f */
+            linenoiseEditMoveRight(&ls);
+            break;
+        case CTRL_P:    /* ctrl-p */
+            linenoiseEditHistoryNext(&ls, LINENOISE_HISTORY_PREV);
+            break;
+        case CTRL_N:    /* ctrl-n */
+            linenoiseEditHistoryNext(&ls, LINENOISE_HISTORY_NEXT);
+            break;
+        case ESC:    /* escape sequence */
+            /* Read the next two bytes representing the escape sequence.
+             * Use two calls to handle slow terminals returning the two
+             * chars at different times. */
+            if (read(ls.ifd,seq,1) == -1) break;
+            if (read(ls.ifd,seq+1,1) == -1) break;
+
+            /* ESC [ sequences. */
+            if (seq[0] == '[') {
+                if (seq[1] >= '0' && seq[1] <= '9') {
+                    /* Extended escape, read additional byte. */
+                    if (read(ls.ifd,seq+2,1) == -1) break;
+                    if (seq[2] == '~') {
+                        switch(seq[1]) {
+                        case '3': /* Delete key. */
+                            linenoiseEditDelete(&ls);
+                            break;
+                        }
+                    }
+                } else {
+                    switch(seq[1]) {
+                    case 'A': /* Up */
+                        linenoiseEditHistoryNext(&ls, LINENOISE_HISTORY_PREV);
+                        break;
+                    case 'B': /* Down */
+                        linenoiseEditHistoryNext(&ls, LINENOISE_HISTORY_NEXT);
+                        break;
+                    case 'C': /* Right */
+                        linenoiseEditMoveRight(&ls);
+                        break;
+                    case 'D': /* Left */
+                        linenoiseEditMoveLeft(&ls);
+                        break;
+                    case 'H': /* Home */
+                        linenoiseEditMoveHome(&ls);
+                        break;
+                    case 'F': /* End*/
+                        linenoiseEditMoveEnd(&ls);
+                        break;
+                    }
+                }
+            }
+
+            /* ESC O sequences. */
+            else if (seq[0] == 'O') {
+                switch(seq[1]) {
+                case 'H': /* Home */
+                    linenoiseEditMoveHome(&ls);
+                    break;
+                case 'F': /* End*/
+                    linenoiseEditMoveEnd(&ls);
+                    break;
+                }
+            }
+            break;
+        default:
+            if (linenoiseEditInsert(&ls,c)) return -1;
+            break;
+        case CTRL_U: /* Ctrl+u, delete the whole line. */
+            buf[0] = '\0';
+            ls.pos = ls.len = 0;
+            linenoiseRefreshLine();
+            break;
+        case CTRL_K: /* Ctrl+k, delete from current to end of line. */
+            buf[ls.pos] = '\0';
+            ls.len = ls.pos;
+            linenoiseRefreshLine();
+            break;
+        case CTRL_A: /* Ctrl+a, go to the start of the line */
+            linenoiseEditMoveHome(&ls);
+            break;
+        case CTRL_E: /* ctrl+e, go to the end of the line */
+            linenoiseEditMoveEnd(&ls);
+            break;
+        case CTRL_L: /* ctrl+l, clear screen */
+            linenoiseClearScreen();
+            linenoiseRefreshLine();
+            break;
+        case CTRL_W: /* ctrl+w, delete previous word */
+            linenoiseEditDeletePrevWord(&ls);
+            break;
+        }
+    }
+    return ls.len;
+}
+
+/* This special mode is used by linenoise in order to print scan codes
+ * on screen for debugging / development purposes. It is implemented
+ * by the linenoise_example program using the --keycodes option. */
+void linenoisePrintKeyCodes(void) {
+    char quit[4];
+
+    printf("Linenoise key codes debugging mode.\n"
+            "Press keys to see scan codes. Type 'quit' at any time to exit.\n");
+    if (linenoiseEnableRawMode(STDIN_FILENO) == -1) return;
+    memset(quit,' ',4);
+    while(1) {
+        char c;
+        int nread;
+
+        nread = read(STDIN_FILENO,&c,1);
+        if (nread <= 0) continue;
+        memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
+        quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
+        if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
+
+        printf("'%c' %02x (%d) (type quit to exit)\n",
+            isprint(c) ? c : '?', (int)c, (int)c);
+        printf("\r"); /* Go left edge manually, we are in raw mode. */
+        fflush(stdout);
+    }
+    linenoiseDisableRawMode(STDIN_FILENO);
+}
+
+/* This function calls the line editing function linenoiseEdit() using
+ * the STDIN file descriptor set in raw mode. */
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+    int count;
+
+    if (buflen == 0) {
+        errno = EINVAL;
+        return -1;
+    }
+    if (!isatty(STDIN_FILENO)) {
+        /* Not a tty: read from file / pipe. */
+        if (fgets(buf, buflen, stdin) == NULL) return -1;
+        count = strlen(buf);
+        if (count && buf[count-1] == '\n') {
+            count--;
+            buf[count] = '\0';
+        }
+    } else {
+        /* Interactive editing. */
+        if (linenoiseEnableRawMode(STDIN_FILENO) == -1) return -1;
+        count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
+        linenoiseDisableRawMode(STDIN_FILENO);
+        printf("\n");
+    }
+    return count;
+}
+
+/* The high level function that is the main API of the linenoise library.
+ * This function checks if the terminal has basic capabilities, just checking
+ * for a blacklist of stupid terminals, and later either calls the line
+ * editing function or uses dummy fgets() so that you will be able to type
+ * something even in the most desperate of the conditions. */
+char *linenoise(const char *prompt) {
+    char buf[LINENOISE_MAX_LINE];
+    int count;
+
+    if (isUnsupportedTerm()) {
+        size_t len;
+
+        printf("%s",prompt);
+        fflush(stdout);
+        if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
+        len = strlen(buf);
+        while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
+            len--;
+            buf[len] = '\0';
+        }
+        return strdup(buf);
+    } else {
+        count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
+        if (count == -1) return NULL;
+        return strdup(buf);
+    }
+}
+
+/* ================================ History ================================= */
+
+/* Free the history, but does not reset it. Only used when we have to
+ * exit() to avoid memory leaks are reported by valgrind & co. */
+static void freeHistory(void) {
+    if (history) {
+        int j;
+
+        for (j = 0; j < history_len; j++)
+            free(history[j]);
+        free(history);
+    }
+}
+
+/* At exit we'll try to fix the terminal to the initial conditions. */
+static void linenoiseAtExit(void) {
+    linenoiseDisableRawMode(STDIN_FILENO);
+    freeHistory();
+}
+
+/* This is the API call to add a new entry in the linenoise history.
+ * It uses a fixed array of char pointers that are shifted (memmoved)
+ * when the history max length is reached in order to remove the older
+ * entry and make room for the new one, so it is not exactly suitable for huge
+ * histories, but will work well for a few hundred of entries.
+ *
+ * Using a circular buffer is smarter, but a bit more complex to handle. */
+int linenoiseHistoryAdd(const char *line) {
+    char *linecopy;
+
+    if (history_max_len == 0) return 0;
+
+    /* Initialization on first call. */
+    if (history == NULL) {
+        history = malloc(sizeof(char*)*history_max_len);
+        if (history == NULL) return 0;
+        memset(history,0,(sizeof(char*)*history_max_len));
+    }
+
+    /* Don't add duplicated lines. */
+    if (history_len && !strcmp(history[history_len-1], line)) return 0;
+
+    /* Add an heap allocated copy of the line in the history.
+     * If we reached the max length, remove the older line. */
+    linecopy = strdup(line);
+    if (!linecopy) return 0;
+    if (history_len == history_max_len) {
+        free(history[0]);
+        memmove(history,history+1,sizeof(char*)*(history_max_len-1));
+        history_len--;
+    }
+    history[history_len] = linecopy;
+    history_len++;
+    return 1;
+}
+
+/* Set the maximum length for the history. This function can be called even
+ * if there is already some history, the function will make sure to retain
+ * just the latest 'len' elements if the new history length value is smaller
+ * than the amount of items already inside the history. */
+int linenoiseHistorySetMaxLen(int len) {
+    char **new;
+
+    if (len < 1) return 0;
+    if (history) {
+        int tocopy = history_len;
+
+        new = malloc(sizeof(char*)*len);
+        if (new == NULL) return 0;
+
+        /* If we can't copy everything, free the elements we'll not use. */
+        if (len < tocopy) {
+            int j;
+
+            for (j = 0; j < tocopy-len; j++) free(history[j]);
+            tocopy = len;
+        }
+        memset(new,0,sizeof(char*)*len);
+        memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
+        free(history);
+        history = new;
+    }
+    history_max_len = len;
+    if (history_len > history_max_len)
+        history_len = history_max_len;
+    return 1;
+}
+
+/* Save the history in the specified file. On success 0 is returned
+ * otherwise -1 is returned. */
+int linenoiseHistorySave(const char *filename) {
+    FILE *fp = fopen(filename,"w");
+    int j;
+
+    if (fp == NULL) return -1;
+    for (j = 0; j < history_len; j++)
+        fprintf(fp,"%s\n",history[j]);
+    fclose(fp);
+    return 0;
+}
+
+/* Load the history from the specified file. If the file does not exist
+ * zero is returned and no operation is performed.
+ *
+ * If the file exists and the operation succeeded 0 is returned, otherwise
+ * on error -1 is returned. */
+int linenoiseHistoryLoad(const char *filename) {
+    FILE *fp = fopen(filename,"r");
+    char buf[LINENOISE_MAX_LINE];
+
+    if (fp == NULL) return -1;
+
+    while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
+        char *p;
+
+        p = strchr(buf,'\r');
+        if (!p) p = strchr(buf,'\n');
+        if (p) *p = '\0';
+        linenoiseHistoryAdd(buf);
+    }
+    fclose(fp);
+    return 0;
+}
diff --git a/tools/lint/linenoise/linenoise.h b/tools/lint/linenoise/linenoise.h
new file mode 100644
index 0000000..c708990
--- /dev/null
+++ b/tools/lint/linenoise/linenoise.h
@@ -0,0 +1,92 @@
+/* linenoise.h -- VERSION 1.0
+ *
+ * Guerrilla line editing library against the idea that a line editing lib
+ * needs to be 20,000 lines of C code.
+ *
+ * See linenoise.c for more information.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  *  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *  *  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LINENOISE_H
+#define __LINENOISE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct linenoiseState {
+    int ifd;            /* Terminal stdin file descriptor. */
+    int ofd;            /* Terminal stdout file descriptor. */
+    char *buf;          /* Edited line buffer. */
+    size_t buflen;      /* Edited line buffer size. */
+    const char *prompt; /* Prompt to display. */
+    size_t plen;        /* Prompt length. */
+    size_t pos;         /* Current cursor position. */
+    size_t oldpos;      /* Previous refresh cursor position. */
+    size_t len;         /* Current edited line length. */
+    size_t cols;        /* Number of columns in terminal. */
+    size_t maxrows;     /* Maximum num of rows used so far (multiline mode) */
+    int rawmode;
+    int history_index;  /* The history index we are currently editing. */
+};
+
+extern struct linenoiseState ls;
+
+typedef struct linenoiseCompletions {
+    int path;
+    size_t len;
+    char **cvec;
+} linenoiseCompletions;
+
+typedef void(linenoiseCompletionCallback)(const char *, const char *, linenoiseCompletions *);
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
+void linenoiseAddCompletion(linenoiseCompletions *, const char *);
+
+char *linenoise(const char *prompt);
+int linenoiseHistoryAdd(const char *line);
+int linenoiseHistorySetMaxLen(int len);
+int linenoiseHistorySave(const char *filename);
+int linenoiseHistoryLoad(const char *filename);
+void linenoiseClearScreen(void);
+void linenoiseSetMultiLine(int ml);
+void linenoisePrintKeyCodes(void);
+
+void linenoisePathCompletion(const char *, const char *, linenoiseCompletions *);
+void linenoiseRefreshLine(void);
+int linenoiseEnableRawMode(int fd);
+void linenoiseDisableRawMode(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LINENOISE_H */
diff --git a/tools/lint/main.c b/tools/lint/main.c
new file mode 100644
index 0000000..6b257da
--- /dev/null
+++ b/tools/lint/main.c
@@ -0,0 +1,111 @@
+/**
+ * @file main.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's yanglint tool
+ *
+ * Copyright (c) 2015-2017 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 "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "commands.h"
+#include "configuration.h"
+#include "completion.h"
+#include "./linenoise/linenoise.h"
+#include "libyang.h"
+
+int done;
+struct ly_ctx *ctx = NULL;
+
+/* main_ni.c */
+int main_ni(int argc, char *argv[]);
+
+int
+main(int argc, char* argv[])
+{
+    char *cmd, *cmdline, *cmdstart;
+    int i, j;
+
+    if (argc > 1) {
+        /* run in non-interactive mode */
+        return main_ni(argc, argv);
+    }
+
+    /* continue in interactive mode */
+    linenoiseSetCompletionCallback(complete_cmd);
+    load_config();
+
+    if (ly_ctx_new(NULL, 0, &ctx)) {
+        fprintf(stderr, "Failed to create context.\n");
+        return 1;
+    }
+
+    while (!done) {
+        /* get the command from user */
+        cmdline = linenoise(PROMPT);
+
+        /* EOF -> exit */
+        if (cmdline == NULL) {
+            done = 1;
+            cmdline = strdup("quit");
+        }
+
+        /* empty line -> wait for another command */
+        if (*cmdline == '\0') {
+            free(cmdline);
+            continue;
+        }
+
+        /* isolate the command word. */
+        for (i = 0; cmdline[i] && (cmdline[i] == ' '); i++);
+        cmdstart = cmdline + i;
+        for (j = 0; cmdline[i] && (cmdline[i] != ' '); i++, j++);
+        cmd = strndup(cmdstart, j);
+
+        /* parse the command line */
+        for (i = 0; commands[i].name; i++) {
+            if (strcmp(cmd, commands[i].name) == 0) {
+                break;
+            }
+        }
+
+        /* execute the command if any valid specified */
+        if (commands[i].name) {
+            /* display help */
+            if ((strchr(cmdstart, ' ') != NULL) && ((strncmp(strchr(cmdstart, ' ')+1, "-h", 2) == 0)
+                    || (strncmp(strchr(cmdstart, ' ')+1, "--help", 6) == 0))) {
+                if (commands[i].help_func != NULL) {
+                    commands[i].help_func();
+                } else {
+                    printf("%s\n", commands[i].helpstring);
+                }
+            } else {
+                commands[i].func((const char *)cmdstart);
+            }
+        } else {
+            /* if unknown command specified, tell it to user */
+            fprintf(stderr, "%s: no such command, type 'help' for more information.\n", cmd);
+        }
+        linenoiseHistoryAdd(cmdline);
+
+        free(cmd);
+        free(cmdline);
+    }
+
+    store_config();
+    ly_ctx_destroy(ctx, NULL);
+
+    return 0;
+}
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
new file mode 100644
index 0000000..8b72e15
--- /dev/null
+++ b/tools/lint/main_ni.c
@@ -0,0 +1,1114 @@
+/**
+ * @file main_ni.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's yanglint tool - noninteractive code
+ *
+ * Copyright (c) 2015-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 "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "commands.h"
+#include "libyang.h"
+
+volatile uint8_t verbose = 0;
+
+#if 0
+/* from commands.c */
+int print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat);
+#endif
+
+void
+help(int shortout)
+{
+    fprintf(stdout, "Usage:\n");
+    fprintf(stdout, "    yanglint [options] [-f { yang | yin | tree | tree-rfc | jsons}] <file>...\n");
+    fprintf(stdout, "        Validates the YANG module in <file>, and all its dependencies.\n\n");
+    fprintf(stdout, "    yanglint [options] [-f { xml | json }] <schema>... <file>...\n");
+    fprintf(stdout, "        Validates the YANG modeled data in <file> according to the <schema>.\n\n");
+    fprintf(stdout, "    yanglint\n");
+    fprintf(stdout, "        Starts interactive mode with more features.\n\n");
+
+    if (shortout) {
+        return;
+    }
+    fprintf(stdout, "Options:\n"
+        "  -h, --help            Show this help message and exit.\n"
+        "  -v, --version         Show version number and exit.\n"
+        "  -V, --verbose         Show verbose messages, can be used multiple times to\n"
+        "                        increase verbosity.\n"
+#ifndef NDEBUG
+        "  -G GROUPS, --debug=GROUPS\n"
+        "                        Enable printing of specific debugging message group\n"
+        "                        (nothing will be printed unless verbosity is set to debug):\n"
+        "                        <group>[,<group>]* (dict, yang, yin, xpath, diff)\n\n"
+#endif
+        "  -p PATH, --path=PATH  Search path for schema (YANG/YIN) modules. The option can be used multiple times.\n"
+        "                        Current working directory and path of the module being added is used implicitly.\n\n"
+        "  -D, --disable-searchdir\n"
+        "                        Do not implicitly search in CWD for schema modules. If specified a second time,\n"
+        "                        do not even search the module directory (all modules must be explicitly specified).\n\n"
+        "  -s, --strict          Strict data parsing (do not skip unknown data),\n"
+        "                        has no effect for schemas.\n\n"
+        "  -m, --merge           Merge input data files into a single tree and validate at once,\n"
+        "                        has no effect for the auto, rpc, rpcreply and notif TYPEs.\n\n"
+        "  -f FORMAT, --format=FORMAT\n"
+        "                        Convert to FORMAT. Supported formats: \n"
+        "                        yang, yin, tree and jsons (JSON) for schemas,\n"
+        "                        xml, json for data.\n"
+        "  -a, --auto            Modify the xml output by adding envelopes for autodetection.\n\n"
+        "  -i, --allimplemented  Make all the imported modules implemented.\n\n"
+        "  -l, --list            Print info about the loaded schemas in ietf-yang-library format,\n"
+        "                        the -f option applies here to specify data encoding.\n"
+        "                        (i - imported module, I - implemented module)\n\n"
+        "  -o OUTFILE, --output=OUTFILE\n"
+        "                        Write the output to OUTFILE instead of stdout.\n\n"
+        "  -F FEATURES, --features=FEATURES\n"
+        "                        Features to support, default all.\n"
+        "                        <modname>:[<feature>,]*\n\n"
+        "  -d MODE, --default=MODE\n"
+        "                        Print data with default values, according to the MODE\n"
+        "                        (to print attributes, ietf-netconf-with-defaults model\n"
+        "                        must be loaded):\n"
+        "        all             - Add missing default nodes.\n"
+        "        all-tagged      - Add missing default nodes and mark all the default\n"
+        "                          nodes with the attribute.\n"
+        "        trim            - Remove all nodes with a default value.\n"
+        "        implicit-tagged - Add missing nodes and mark them with the attribute.\n\n"
+        "  -t TYPE, --type=TYPE\n"
+        "                        Specify data tree type in the input data file:\n"
+        "        auto            - Resolve data type (one of the following) automatically\n"
+        "                          (as pyang does) - applicable only on XML input data.\n"
+        "        data            - Complete datastore with status data (default type).\n"
+        "        config          - Configuration datastore (without status data).\n"
+        "        get             - Result of the NETCONF <get> operation.\n"
+        "        getconfig       - Result of the NETCONF <get-config> operation.\n"
+        "        edit            - Content of the NETCONF <edit-config> operation.\n"
+        "        rpc             - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.\n"
+        "        rpcreply        - Reply to the RPC. The input data <file>s are expected in pairs - each RPC reply\n"
+        "                          input data <file> must be followed by the origin RPC input data <file> for the reply.\n"
+        "                          The same rule of pairing applies also in case of 'auto' TYPE and input data file\n"
+        "                          containing RPC reply.\n"
+        "        notif           - Notification instance (content of the <notification> element without <eventTime>.\n\n"
+        "  -O FILE, --operational=FILE\n"
+        "                        - Optional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the FILE contains running\n"
+        "                          configuration datastore and state data (operational datastore) referenced from\n"
+        "                          the RPC/Notification. The same data apply to all input data <file>s. Note that\n"
+        "                          the file is validated as 'data' TYPE. Special value '!' can be used as FILE argument\n"
+        "                          to ignore the external references.\n\n"
+        "  -y YANGLIB_PATH       - Path to a yang-library data describing the initial context.\n\n"
+        "Tree output specific options:\n"
+        "  --tree-help           - Print help on tree symbols and exit.\n"
+        "  --tree-print-groupings\n"
+        "                        Print top-level groupings in a separate section.\n"
+        "  --tree-print-uses     - Print uses nodes instead the resolved grouping nodes.\n"
+        "  --tree-no-leafref-target\n"
+        "                        Do not print target nodes of leafrefs.\n"
+        "  --tree-path=SCHEMA_PATH\n"
+        "                        Print only the specified subtree.\n"
+        "  --tree-line-length=LINE_LENGTH\n"
+        "                        Wrap lines if longer than the specified length (it is not a strict limit, longer lines\n"
+        "                        can often appear).\n"
+        "\n");
+}
+
+void
+tree_help(void)
+{
+    fprintf(stdout, "Each node is printed as:\n\n");
+    fprintf(stdout, "<status> <flags> <name> <opts> <type> <if-features>\n\n"
+                    "  <status> is one of:\n"
+                    "    + for current\n"
+                    "    x for deprecated\n"
+                    "    o for obsolete\n\n"
+                    "  <flags> is one of:\n"
+                    "    rw for configuration data\n"
+                    "    ro for status data\n"
+                    "    -x for RPCs\n"
+                    "    -n for Notification\n\n"
+                    "  <name> is the name of the node\n"
+                    "    (<name>) means that the node is a choice node\n"
+                    "    :(<name>) means that the node is a case node\n\n"
+                    "    if the node is augmented into the tree from another module,\n"
+                    "    it is printed with the module name as <module-name>:<name>.\n\n"
+                    "  <opts> is one of:\n"
+                    "    ? for an optional leaf or choice\n"
+                    "    ! for a presence container\n"
+                    "    * for a leaf-list or list\n"
+                    "    [<keys>] for a list's keys\n\n"
+                    "  <type> is the name of the type for leafs and leaf-lists\n"
+                    "    If there is a default value defined, it is printed within\n"
+                    "    angle brackets <default-value>.\n"
+                    "    If the type is a leafref, the type is printed as -> TARGET`\n\n"
+                    "  <if-features> is the list of features this node depends on,\n"
+                    "    printed within curly brackets and a question mark {...}?\n\n");
+}
+
+void
+version(void)
+{
+    fprintf(stdout, "yanglint %s\n", PROJECT_VERSION);
+}
+
+void
+libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+    char *levstr;
+
+    if (level <= verbose) {
+        switch(level) {
+        case LY_LLERR:
+            levstr = "err :";
+            break;
+        case LY_LLWRN:
+            levstr = "warn:";
+            break;
+        case LY_LLVRB:
+            levstr = "verb:";
+            break;
+        default:
+            levstr = "dbg :";
+            break;
+        }
+        if (path) {
+            fprintf(stderr, "%s %s (%s)\n", levstr, msg, path);
+        } else {
+            fprintf(stderr, "%s %s\n", levstr, msg);
+        }
+    }
+}
+
+/*
+ * return:
+ * 0 - error
+ * 1 - schema format
+ * 2 - data format
+ */
+static int
+get_fileformat(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
+{
+    char *ptr;
+    LYS_INFORMAT informat_s;
+    LYD_FORMAT informat_d;
+
+    /* get the file format */
+    if ((ptr = strrchr(filename, '.')) != NULL) {
+        ++ptr;
+        if (!strcmp(ptr, "yang")) {
+            informat_s = LYS_IN_YANG;
+            informat_d = 0;
+#if 0
+        } else if (!strcmp(ptr, "yin")) {
+            informat_s = LYS_IN_YIN;
+            informat_d = 0;
+#endif
+        } else if (!strcmp(ptr, "xml")) {
+            informat_s = 0;
+            informat_d = LYD_XML;
+#if 0
+        } else if (!strcmp(ptr, "json")) {
+            informat_s = 0;
+            informat_d = LYD_JSON;
+#endif
+        } else {
+            fprintf(stderr, "yanglint error: input file in an unknown format \"%s\".\n", ptr);
+            return 0;
+        }
+    } else {
+        fprintf(stderr, "yanglint error: input file \"%s\" without file extension - unknown format.\n", filename);
+        return 0;
+    }
+
+    if (data) {
+        (*data) = informat_d;
+    }
+
+    if (schema) {
+        (*schema) = informat_s;
+    }
+
+    if (informat_s) {
+        return 1;
+    } else {
+        return 2;
+    }
+}
+
+int
+main_ni(int argc, char* argv[])
+{
+    int ret = EXIT_FAILURE;
+    int opt, opt_index = 0, i, featsize = 0, compiled = 0;
+    struct option options[] = {
+#if 0
+        {"auto",             no_argument,       NULL, 'a'},
+        {"default",          required_argument, NULL, 'd'},
+#endif
+        {"format",           required_argument, NULL, 'f'},
+        {"features",         required_argument, NULL, 'F'},
+#if 0
+        {"tree-print-groupings", no_argument,   NULL, 'g'},
+        {"tree-print-uses",  no_argument,       NULL, 'u'},
+        {"tree-no-leafref-target", no_argument, NULL, 'n'},
+        {"tree-path",        required_argument, NULL, 'P'},
+        {"tree-line-length", required_argument, NULL, 'L'},
+#endif
+        {"compiled",         no_argument,       NULL, 'c'},
+        {"help",             no_argument,       NULL, 'h'},
+#if 0
+        {"tree-help",        no_argument,       NULL, 'H'},
+#endif
+        {"allimplemented",   no_argument,       NULL, 'i'},
+        {"disable-cwd-search", no_argument,     NULL, 'D'},
+        {"list",             no_argument,       NULL, 'l'},
+#if 0
+        {"merge",            no_argument,       NULL, 'm'},
+#endif
+        {"output",           required_argument, NULL, 'o'},
+        {"path",             required_argument, NULL, 'p'},
+#if 0
+        {"running",          required_argument, NULL, 'r'},
+        {"operational",      required_argument, NULL, 'O'},
+#endif
+        {"strict",           no_argument,       NULL, 's'},
+        {"type",             required_argument, NULL, 't'},
+        {"version",          no_argument,       NULL, 'v'},
+        {"verbose",          no_argument,       NULL, 'V'},
+#ifndef NDEBUG
+        {"debug",            required_argument, NULL, 'G'},
+#endif
+        {NULL,               required_argument, NULL, 'y'},
+        {NULL,               0,                 NULL, 0}
+    };
+    FILE *out = stdout;
+    struct ly_ctx *ctx = NULL;
+    const struct lys_module *mod;
+    LYS_OUTFORMAT outformat_s = 0;
+    LYS_INFORMAT informat_s;
+    LYD_FORMAT informat_d, outformat_d = 0;
+#if 0
+    LYD_FORMAT ylformat = 0;
+#endif
+    struct ly_set *searchpaths = NULL;
+    const char *outtarget_s = NULL;
+    char **feat = NULL, *ptr, *featlist, *dir;
+    struct stat st;
+    uint32_t u;
+    int options_ctx = LY_CTX_NOYANGLIBRARY, list = 0, outoptions_s = 0, outline_length_s = 0;
+    int autodetection = 0, options_parser = 0, merge = 0;
+    const char *oper_file = NULL;
+#if 0
+    const char *envelope_s = NULL;
+    char *ylpath = NULL;
+    int options_dflt = 0, envelope = 0;
+    struct lyxml_elem *iter, *elem;
+    struct *subroot, *next, *node;
+#endif
+    struct lyd_node *oper = NULL;
+    struct dataitem {
+        const char *filename;
+        struct lyd_node *tree;
+        struct dataitem *next;
+        LYD_FORMAT format;
+        int type;
+    } *data = NULL, *data_item, *data_prev = NULL;
+    struct ly_set *mods = NULL;
+    void *p;
+    int index = 0;
+
+    opterr = 0;
+#ifndef NDEBUG
+    while ((opt = getopt_long(argc, argv, "acd:f:F:gunP:L:hHiDlmo:p:r:O:st:vVG:y:", options, &opt_index)) != -1)
+#else
+    while ((opt = getopt_long(argc, argv, "acd:f:F:gunP:L:hHiDlmo:p:r:O:st:vVy:", options, &opt_index)) != -1)
+#endif
+    {
+        switch (opt) {
+#if 0
+        case 'a':
+            envelope = 1;
+            break;
+        case 'd':
+            if (!strcmp(optarg, "all")) {
+                options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL;
+            } else if (!strcmp(optarg, "all-tagged")) {
+                options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
+            } else if (!strcmp(optarg, "trim")) {
+                options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_TRIM;
+            } else if (!strcmp(optarg, "implicit-tagged")) {
+                options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
+            } else {
+                fprintf(stderr, "yanglint error: unknown default mode %s\n", optarg);
+                help(1);
+                goto cleanup;
+            }
+            break;
+#endif
+        case 'c':
+            compiled = 1;
+            break;
+        case 'f':
+            if (!strcasecmp(optarg, "yang")) {
+                outformat_s = LYS_OUT_YANG;
+                outformat_d = 0;
+#if 0
+            } else if (!strcasecmp(optarg, "tree")) {
+                outformat_s = LYS_OUT_TREE;
+                outformat_d = 0;
+            } else if (!strcasecmp(optarg, "tree-rfc")) {
+                outformat_s = LYS_OUT_TREE;
+                outoptions_s |= LYS_OUTOPT_TREE_RFC;
+                outformat_d = 0;
+            } else if (!strcasecmp(optarg, "yin")) {
+                outformat_s = LYS_OUT_YIN;
+                outformat_d = 0;
+            } else if (!strcasecmp(optarg, "jsons")) {
+                outformat_s = LYS_OUT_JSON;
+                outformat_d = 0;
+#endif
+            } else if (!strcasecmp(optarg, "xml")) {
+                outformat_s = 0;
+                outformat_d = LYD_XML;
+#if 0
+            } else if (!strcasecmp(optarg, "json")) {
+                outformat_s = 0;
+                outformat_d = LYD_JSON;
+#endif
+            } else {
+                fprintf(stderr, "yanglint error: unknown output format %s\n", optarg);
+                help(1);
+                goto cleanup;
+            }
+            break;
+        case 'F':
+            featsize++;
+            if (!feat) {
+                p = malloc(sizeof *feat);
+            } else {
+                p = realloc(feat, featsize * sizeof *feat);
+            }
+            if (!p) {
+                fprintf(stderr, "yanglint error: Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+                goto cleanup;
+            }
+            feat = p;
+            feat[featsize - 1] = strdup(optarg);
+            ptr = strchr(feat[featsize - 1], ':');
+            if (!ptr) {
+                fprintf(stderr, "yanglint error: Invalid format of the features specification (%s)", optarg);
+                goto cleanup;
+            }
+            *ptr = '\0';
+
+            break;
+#if 0
+        case 'g':
+            outoptions_s |= LYS_OUTOPT_TREE_GROUPING;
+            break;
+        case 'u':
+            outoptions_s |= LYS_OUTOPT_TREE_USES;
+            break;
+        case 'n':
+            outoptions_s |= LYS_OUTOPT_TREE_NO_LEAFREF;
+            break;
+        case 'P':
+            outtarget_s = optarg;
+            break;
+        case 'L':
+            outline_length_s = atoi(optarg);
+            break;
+#endif
+        case 'h':
+            help(0);
+            ret = EXIT_SUCCESS;
+            goto cleanup;
+#if 0
+        case 'H':
+            tree_help();
+            ret = EXIT_SUCCESS;
+            goto cleanup;
+#endif
+        case 'i':
+            options_ctx |= LY_CTX_ALLIMPLEMENTED;
+            break;
+        case 'D':
+            if (options_ctx & LY_CTX_DISABLE_SEARCHDIRS) {
+                fprintf(stderr, "yanglint error: -D specified too many times.\n");
+                goto cleanup;
+            } else if (options_ctx & LY_CTX_DISABLE_SEARCHDIR_CWD) {
+                options_ctx &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
+                options_ctx |= LY_CTX_DISABLE_SEARCHDIRS;
+            } else {
+                options_ctx |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+            }
+            break;
+        case 'l':
+            list = 1;
+            break;
+#if 0
+        case 'm':
+            merge = 1;
+            break;
+#endif
+        case 'o':
+            if (out != stdout) {
+                fclose(out);
+            }
+            out = fopen(optarg, "w");
+            if (!out) {
+                fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
+                goto cleanup;
+            }
+            break;
+        case 'p':
+            if (stat(optarg, &st) == -1) {
+                fprintf(stderr, "yanglint error: Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
+                goto cleanup;
+            }
+            if (!S_ISDIR(st.st_mode)) {
+                fprintf(stderr, "yanglint error: Provided search path is not a directory.\n");
+                goto cleanup;
+            }
+            if (!searchpaths) {
+                searchpaths = ly_set_new();
+            }
+            ly_set_add(searchpaths, optarg, 0);
+            break;
+#if 0
+        case 'r':
+        case 'O':
+            if (oper_file || (options_parser & LYD_OPT_NOEXTDEPS)) {
+                fprintf(stderr, "yanglint error: The operational datastore (-O) cannot be set multiple times.\n");
+                goto cleanup;
+            }
+            if (optarg[0] == '!') {
+                /* ignore extenral dependencies to the operational datastore */
+                options_parser |= LYD_OPT_NOEXTDEPS;
+            } else {
+                /* external file with the operational datastore */
+                oper_file = optarg;
+            }
+            break;
+#endif
+        case 's':
+            options_parser |= LYD_OPT_STRICT;
+            break;
+        case 't':
+            if (!strcmp(optarg, "auto")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK);
+                autodetection = 1;
+            } else if (!strcmp(optarg, "config")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
+            } else if (!strcmp(optarg, "get")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
+            } else if (!strcmp(optarg, "getconfig")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
+            } else if (!strcmp(optarg, "edit")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
+            } else if (!strcmp(optarg, "data")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
+            } else if (!strcmp(optarg, "rpc")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
+            } else if (!strcmp(optarg, "rpcreply")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
+            } else if (!strcmp(optarg, "notif")) {
+                options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
+            } else {
+                fprintf(stderr, "yanglint error: unknown data tree type %s\n", optarg);
+                help(1);
+                goto cleanup;
+            }
+            break;
+        case 'v':
+            version();
+            ret = EXIT_SUCCESS;
+            goto cleanup;
+        case 'V':
+            verbose++;
+            break;
+#ifndef NDEBUG
+        case 'G':
+            u = 0;
+            ptr = optarg;
+            while (ptr[0]) {
+                if (!strncmp(ptr, "dict", 4)) {
+                    u |= LY_LDGDICT;
+                    ptr += 4;
+                } else if (!strncmp(ptr, "yang", 4)) {
+                    u |= LY_LDGYANG;
+                    ptr += 4;
+                } else if (!strncmp(ptr, "yin", 3)) {
+                    u |= LY_LDGYIN;
+                    ptr += 3;
+                } else if (!strncmp(ptr, "xpath", 5)) {
+                    u |= LY_LDGXPATH;
+                    ptr += 5;
+                } else if (!strncmp(ptr, "diff", 4)) {
+                    u |= LY_LDGDIFF;
+                    ptr += 4;
+                }
+
+                if (ptr[0]) {
+                    if (ptr[0] != ',') {
+                        fprintf(stderr, "yanglint error: unknown debug group string \"%s\"\n", optarg);
+                        goto cleanup;
+                    }
+                    ++ptr;
+                }
+            }
+            ly_verb_dbg(u);
+            break;
+#endif
+#if 0
+        case 'y':
+            ptr = strrchr(optarg, '.');
+            if (ptr) {
+                ptr++;
+                if (!strcmp(ptr, "xml")) {
+                    ylformat = LYD_XML;
+                } else if (!strcmp(ptr, "json")) {
+                    ylformat = LYD_JSON;
+                } else {
+                    fprintf(stderr, "yanglint error: yang-library file in an unknown format \"%s\".\n", ptr);
+                    goto cleanup;
+                }
+            } else {
+                fprintf(stderr, "yanglint error: yang-library file in an unknown format.\n");
+                goto cleanup;
+            }
+            ylpath = optarg;
+            break;
+#endif
+        default:
+            help(1);
+            if (optopt) {
+                fprintf(stderr, "yanglint error: invalid option: -%c\n", optopt);
+            } else {
+                fprintf(stderr, "yanglint error: invalid option: %s\n", argv[optind - 1]);
+            }
+            goto cleanup;
+        }
+    }
+
+    /* check options compatibility */
+    if (!list && optind >= argc) {
+        help(1);
+        fprintf(stderr, "yanglint error: missing <file> to process\n");
+        goto cleanup;
+    }
+    if (compiled) {
+        if (!outformat_s) {
+            fprintf(stderr, "yanglint warning: --compiled option takes effect only in case of printing schemas.\n");
+        } else {
+            outformat_s++;
+        }
+    }
+    if (outformat_s && outformat_s != LYS_OUT_TREE && (optind + 1) < argc) {
+        /* we have multiple schemas to be printed as YIN or YANG */
+        fprintf(stderr, "yanglint error: too many schemas to convert and store.\n");
+        goto cleanup;
+    }
+    if (outoptions_s || outtarget_s || outline_length_s) {
+#if 0
+        if (outformat_d || (outformat_s && outformat_s != LYS_OUT_TREE)) {
+            /* we have --tree-print-grouping with other output format than tree */
+            fprintf(stderr,
+                    "yanglint warning: --tree options take effect only in case of the tree output format.\n");
+        }
+    }
+    if (merge) {
+        if (autodetection || (options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF))) {
+            fprintf(stderr, "yanglint warning: merging not allowed, ignoring option -m.\n");
+            merge = 0;
+        } else {
+            /* first, files will be parsed as trusted to allow missing data, then the data trees will be merged
+             * and the result will be validated */
+            options_parser |= LYD_OPT_TRUSTED;
+        }
+#endif
+    }
+#if 0
+    if (!outformat_d && options_dflt) {
+        /* we have options for printing default nodes, but data output not specified */
+        fprintf(stderr, "yanglint warning: default mode is ignored when not printing data.\n");
+    }
+    if (outformat_s && (options_parser || autodetection)) {
+        /* we have options for printing data tree, but output is schema */
+        fprintf(stderr, "yanglint warning: data parser options are ignored when printing schema.\n");
+    }
+    if (oper_file && (!autodetection && !(options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF)))) {
+        fprintf(stderr, "yanglint warning: operational datastore applies only to RPCs or Notifications.\n");
+        /* ignore operational datastore file */
+        oper_file = NULL;
+    }
+#endif
+    if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_DATA) {
+        /* add option to ignore ietf-yang-library data for implicit data type */
+        options_parser |= LYD_OPT_DATA_NO_YANGLIB;
+    }
+
+    /* set callback for printing libyang messages */
+    ly_set_log_clb(libyang_verbclb, 1);
+#if 0
+    /* create libyang context */
+    if (ylpath) {
+        ctx = ly_ctx_new_ylpath(searchpaths ? (const char*)searchpaths->set.g[0] : NULL, ylpath, ylformat, options_ctx);
+    } else {
+#else
+    {
+#endif
+        ly_ctx_new(NULL, options_ctx, &ctx);
+    }
+    if (!ctx) {
+        goto cleanup;
+    }
+
+    /* set searchpaths */
+    if (searchpaths) {
+        for (u = 0; u < searchpaths->count; u++) {
+            ly_ctx_set_searchdir(ctx, (const char*)searchpaths->objs[u]);
+        }
+        index = u + 1;
+    }
+
+    /* derefered setting of verbosity in libyang after context initiation */
+    ly_verb(verbose);
+
+    mods = ly_set_new();
+
+
+    /* divide input files */
+    for (i = 0; i < argc - optind; i++) {
+        /* get the file format */
+        if (!get_fileformat(argv[optind + i], &informat_s, &informat_d)) {
+            goto cleanup;
+        }
+
+        if (informat_s) {
+            /* load/validate schema */
+            if (verbose >= 2) {
+                fprintf(stdout, "Validating %s schema file.\n", argv[optind + i]);
+            }
+            dir = strdup(argv[optind + i]);
+            ly_ctx_set_searchdir(ctx, ptr = dirname(dir));
+            mod = lys_parse_path(ctx, argv[optind + i], informat_s);
+            ly_ctx_unset_searchdir(ctx, index);
+            free(dir);
+            if (!mod) {
+                goto cleanup;
+            }
+            ly_set_add(mods, (void *)mod, 0);
+        } else {
+            if (autodetection && informat_d != LYD_XML) {
+                /* data file content autodetection is possible only for XML input */
+                fprintf(stderr, "yanglint error: data type autodetection is applicable only to XML files.\n");
+                goto cleanup;
+            }
+
+            /* remember data filename and its format */
+            if (!data) {
+                data = data_item = malloc(sizeof *data);
+            } else {
+                for (data_item = data; data_item->next; data_item = data_item->next);
+                data_item->next = malloc(sizeof *data_item);
+                data_item = data_item->next;
+            }
+            data_item->filename = argv[optind + i];
+            data_item->format = informat_d;
+            data_item->type = options_parser & LYD_OPT_TYPEMASK;
+            data_item->tree = NULL;
+            data_item->next = NULL;
+        }
+    }
+    if (outformat_d && !data && !list) {
+        fprintf(stderr, "yanglint error: no input data file for the specified data output format.\n");
+        goto cleanup;
+    }
+
+    /* enable specified features, if not specified, all the module's features are enabled */
+    u = 4; /* skip internal libyang modules */
+    while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
+        for (i = 0; i < featsize; i++) {
+            if (!strcmp(feat[i], mod->name)) {
+                /* parse features spec */
+                featlist = strdup(feat[i] + strlen(feat[i]) + 1);
+                ptr = NULL;
+                while((ptr = strtok(ptr ? NULL : featlist, ","))) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Enabling feature %s in module %s.\n", ptr, mod->name);
+                    }
+                    if (lys_feature_enable(mod, ptr)) {
+                        fprintf(stderr, "Feature %s not defined in module %s.\n", ptr, mod->name);
+                    }
+                }
+                free(featlist);
+                break;
+            }
+        }
+        if (i == featsize) {
+            if (verbose >= 2) {
+                fprintf(stdout, "Enabling all features in module %s.\n", mod->name);
+            }
+            lys_feature_enable(mod, "*");
+        }
+    }
+
+    /* convert (print) to FORMAT */
+    if (outformat_s) {
+        if (outformat_s == LYS_OUT_JSON && mods->count > 1) {
+            fputs("[", out);
+        }
+        for (u = 0; u < mods->count; u++) {
+            if (u) {
+                if (outformat_s == LYS_OUT_JSON) {
+                    fputs(",\n", out);
+                } else {
+                    fputs("\n", out);
+                }
+            }
+            lys_print_file(out, (struct lys_module *)mods->objs[u], outformat_s, outline_length_s, outoptions_s);
+        }
+        if (outformat_s == LYS_OUT_JSON) {
+            if (mods->count > 1) {
+                fputs("]\n", out);
+            } else if (mods->count == 1) {
+                fputs("\n", out);
+            }
+        }
+    } else if (data) {
+
+        /* prepare operational datastore when specified for RPC/Notification */
+        if (oper_file) {
+            /* get the file format */
+            if (!get_fileformat(oper_file, NULL, &informat_d)) {
+                goto cleanup;
+            } else if (!informat_d) {
+                fprintf(stderr, "yanglint error: The operational data are expected in XML or JSON format.\n");
+                goto cleanup;
+            }
+            oper = lyd_parse_path(ctx, oper_file, informat_d, LYD_OPT_DATA_NO_YANGLIB | LYD_OPT_TRUSTED);
+            if (!oper) {
+                fprintf(stderr, "yanglint error: Failed to parse the operational datastore file for RPC/Notification validation.\n");
+                goto cleanup;
+            }
+        }
+
+        for (data_item = data, data_prev = NULL; data_item; data_prev = data_item, data_item = data_item->next) {
+            /* parse data file - via LYD_OPT_TRUSTED postpone validation when all data are loaded and merged */
+#if 0
+            if (autodetection) {
+                /* erase option not covered by LYD_OPT_TYPEMASK, but used according to the type */
+                options_parser &= ~LYD_OPT_DATA_NO_YANGLIB;
+                /* automatically detect data type from the data top level */
+                data_item->xml = lyxml_parse_path(ctx, data_item->filename, 0);
+                if (!data_item->xml) {
+                    fprintf(stderr, "yanglint error: parsing XML data for data type autodetection failed.\n");
+                    goto cleanup;
+                }
+
+                /* NOTE: namespace is ignored to simplify usage of this feature */
+                if (!strcmp(data_item->xml->name, "data")) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Parsing %s as complete datastore.\n", data_item->filename);
+                    }
+                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
+                    data_item->type = LYD_OPT_DATA;
+                } else if (!strcmp(data_item->xml->name, "config")) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Parsing %s as config data.\n", data_item->filename);
+                    }
+                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
+                    data_item->type = LYD_OPT_CONFIG;
+                } else if (!strcmp(data_item->xml->name, "get-reply")) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Parsing %s as <get> reply data.\n", data_item->filename);
+                    }
+                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
+                    data_item->type = LYD_OPT_GET;
+                } else if (!strcmp(data_item->xml->name, "get-config-reply")) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Parsing %s as <get-config> reply data.\n", data_item->filename);
+                    }
+                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
+                    data_item->type = LYD_OPT_GETCONFIG;
+                } else if (!strcmp(data_item->xml->name, "edit-config")) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Parsing %s as <edit-config> data.\n", data_item->filename);
+                    }
+                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
+                    data_item->type = LYD_OPT_EDIT;
+                } else if (!strcmp(data_item->xml->name, "rpc")) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Parsing %s as <rpc> data.\n", data_item->filename);
+                    }
+                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
+                    data_item->type = LYD_OPT_RPC;
+                } else if (!strcmp(data_item->xml->name, "rpc-reply")) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", data_item->filename);
+                    }
+
+                    data_item->type = LYD_OPT_RPCREPLY;
+                    if (!data_item->next || (data_prev && !data_prev->tree)) {
+                        fprintf(stderr, "RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
+                        goto cleanup;
+                    }
+
+                    continue;
+                } else if (!strcmp(data_item->xml->name, "notification")) {
+                    if (verbose >= 2) {
+                        fprintf(stdout, "Parsing %s as <notification> data.\n", data_item->filename);
+                    }
+                    options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
+                    data_item->type = LYD_OPT_NOTIF;
+
+                    /* ignore eventTime element if present */
+                    while (data_item->xml->child && !strcmp(data_item->xml->child->name, "eventTime")) {
+                        lyxml_free(ctx, data_item->xml->child);
+                    }
+                } else {
+                    fprintf(stderr, "yanglint error: invalid top-level element \"%s\" for data type autodetection.\n",
+                            data_item->xml->name);
+                    goto cleanup;
+                }
+
+                data_item->tree = lyd_parse_xml(ctx, &data_item->xml->child, options_parser, oper);
+                if (data_prev && data_prev->type == LYD_OPT_RPCREPLY) {
+parse_reply:
+                    /* check result of the RPC parsing, we are going to do another parsing in this step */
+                    if (ly_errno) {
+                        goto cleanup;
+                    }
+
+                    /* check that we really have RPC for the reply */
+                    if (data_item->type != LYD_OPT_RPC) {
+                        fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_prev->filename);
+                        goto cleanup;
+                    }
+
+                    if (data_prev->format == LYD_XML) {
+                        /* ignore <ok> and <rpc-error> elements if present */
+                        u = 0;
+                        LY_TREE_FOR_SAFE(data_prev->xml->child, iter, elem) {
+                            if (!strcmp(data_prev->xml->child->name, "ok")) {
+                                if (u) {
+                                    /* rpc-error or ok already present */
+                                    u = 0x8; /* error flag */
+                                } else {
+                                    u = 0x1 | 0x4; /* <ok> flag with lyxml_free() flag */
+                                }
+                            } else if (!strcmp(data_prev->xml->child->name, "rpc-error")) {
+                                if (u && (u & 0x1)) {
+                                    /* ok already present, rpc-error can be present multiple times */
+                                    u = 0x8; /* error flag */
+                                } else {
+                                    u = 0x2 | 0x4; /* <rpc-error> flag with lyxml_free() flag */
+                                }
+                            }
+
+                            if (u == 0x8) {
+                                fprintf(stderr, "yanglint error: Invalid RPC reply (%s) content.\n", data_prev->filename);
+                                goto cleanup;
+                            } else if (u & 0x4) {
+                                lyxml_free(ctx, data_prev->xml->child);
+                                u &= ~0x4; /* unset lyxml_free() flag */
+                            }
+                        }
+
+                        /* finally, parse RPC reply from the previous step */
+                        data_prev->tree = lyd_parse_xml(ctx, &data_prev->xml->child,
+                                                        (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, oper);
+                    } else { /* LYD_JSON */
+                        data_prev->tree = lyd_parse_path(ctx, data_prev->filename, data_item->format,
+                                                         (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, oper);
+                    }
+                }
+            } else if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_RPCREPLY) {
+                if (data_prev && !data_prev->tree) {
+                    /* now we should have RPC for the preceding RPC reply */
+                    data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format,
+                                                     (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC, oper);
+                    data_item->type = LYD_OPT_RPC;
+                    goto parse_reply;
+                } else {
+                    /* now we have RPC reply which will be parsed in next step together with its RPC */
+                    if (!data_item->next) {
+                        fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
+                        goto cleanup;
+                    }
+                    if (data_item->format == LYD_XML) {
+                        /* create rpc-reply container to unify handling with autodetection */
+                        data_item->xml = calloc(1, sizeof *data_item->xml);
+                        if (!data_item->xml) {
+                            fprintf(stderr, "yanglint error: Memory allocation failed failed.\n");
+                            goto cleanup;
+                        }
+                        data_item->xml->name = lydict_insert(ctx, "rpc-reply", 9);
+                        data_item->xml->prev = data_item->xml;
+                        data_item->xml->child = lyxml_parse_path(ctx, data_item->filename, LYXML_PARSE_MULTIROOT | LYXML_PARSE_NOMIXEDCONTENT);
+                        if (data_item->xml->child) {
+                            data_item->xml->child->parent = data_item->xml;
+                        }
+                    }
+                    continue;
+                }
+            } else {
+#else
+            {
+#endif
+                data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format, options_parser, oper);
+            }
+            if (ly_err_first(ctx)) {
+                goto cleanup;
+            }
+#if 0
+            if (merge && data != data_item) {
+                if (!data->tree) {
+                    data->tree = data_item->tree;
+                } else if (data_item->tree) {
+                    /* merge results */
+                    if (lyd_merge(data->tree, data_item->tree, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
+                        fprintf(stderr, "yanglint error: merging multiple data trees failed.\n");
+                        goto cleanup;
+                    }
+                }
+                data_item->tree = NULL;
+            }
+#endif
+        }
+#if 0
+        if (merge) {
+            /* validate the merged data tree, do not trust the input, invalidate all the data first */
+            LY_TREE_FOR(data->tree, subroot) {
+                LY_TREE_DFS_BEGIN(subroot, next, node) {
+                    node->validity = LYD_VAL_OK;
+                    switch (node->schema->nodetype) {
+                    case LYS_LEAFLIST:
+                    case LYS_LEAF:
+                        if (((struct lys_node_leaf *)node->schema)->type.base == LY_TYPE_LEAFREF) {
+                            node->validity |= LYD_VAL_LEAFREF;
+                        }
+                        break;
+                    case LYS_LIST:
+                        node->validity |= LYD_VAL_UNIQUE;
+                        /* falls through */
+                    case LYS_CONTAINER:
+                    case LYS_NOTIF:
+                    case LYS_RPC:
+                    case LYS_ACTION:
+                        node->validity |= LYD_VAL_MAND;
+                        break;
+                    default:
+                        break;
+                    }
+                    LY_TREE_DFS_END(subroot, next, node)
+                }
+            }
+            if (lyd_validate(&data->tree, options_parser & ~LYD_OPT_TRUSTED, ctx)) {
+                goto cleanup;
+            }
+        }
+#endif
+        /* print only if data output format specified */
+        if (outformat_d) {
+            for (data_item = data; data_item; data_item = data_item->next) {
+                if (!merge && verbose >= 2) {
+                    fprintf(stdout, "File %s:\n", data_item->filename);
+                }
+#if 0
+                if (outformat_d == LYD_XML && envelope) {
+                    switch (data_item->type) {
+                    case LYD_OPT_DATA:
+                        envelope_s = "data";
+                        break;
+                    case LYD_OPT_CONFIG:
+                        envelope_s = "config";
+                        break;
+                    case LYD_OPT_GET:
+                        envelope_s = "get-reply";
+                        break;
+                    case LYD_OPT_GETCONFIG:
+                        envelope_s = "get-config-reply";
+                        break;
+                    case LYD_OPT_EDIT:
+                        envelope_s = "edit-config";
+                        break;
+                    case LYD_OPT_RPC:
+                        envelope_s = "rpc";
+                        break;
+                    case LYD_OPT_RPCREPLY:
+                        envelope_s = "rpc-reply";
+                        break;
+                    case LYD_OPT_NOTIF:
+                        envelope_s = "notification";
+                        break;
+                    }
+                    fprintf(out, "<%s>\n", envelope_s);
+                    if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
+                        /* action */
+                        fprintf(out, "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">\n");
+                    }
+                }
+#endif
+                lyd_print_file(out, (data_item->type == LYD_OPT_RPCREPLY) ? lyd_node_children(data_item->tree) : data_item->tree,
+                               outformat_d, LYDP_WITHSIBLINGS | LYDP_FORMAT /* TODO defaults | options_dflt */);
+#if 0
+                if (envelope_s) {
+                    if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
+                        fprintf(out, "</action>\n");
+                    }
+                    fprintf(out, "</%s>\n", envelope_s);
+                }
+                if (merge) {
+                    /* stop after first item */
+                    break;
+                }
+#endif
+            }
+        }
+    }
+#if 0
+    if (list) {
+        print_list(out, ctx, outformat_d);
+    }
+#endif
+
+    ret = EXIT_SUCCESS;
+
+cleanup:
+    if (out && out != stdout) {
+        fclose(out);
+    }
+    ly_set_free(mods, NULL);
+    ly_set_free(searchpaths, NULL);
+    for (i = 0; i < featsize; i++) {
+        free(feat[i]);
+    }
+    free(feat);
+    for (; data; data = data_item) {
+        data_item = data->next;
+        lyd_free_all(data->tree);
+        free(data);
+    }
+    lyd_free_all(oper);
+    ly_ctx_destroy(ctx, NULL);
+
+    return ret;
+}
diff --git a/tools/lint/yanglint.1 b/tools/lint/yanglint.1
new file mode 100644
index 0000000..4b7060d
--- /dev/null
+++ b/tools/lint/yanglint.1
@@ -0,0 +1,136 @@
+.\" Manpage for yanglint.
+.\" Process this file with
+.\" groff -man -Tascii yanglint.1
+.\"
+
+.TH YANGLINT 1 "2016-10-27" "libyang"
+.SH NAME
+yanglint \- YANG lint tool
+.
+.SH SYNOPSIS
+.B yanglint
+.br
+.B yanglint
+[\fIOPTIONS\fP]
+[\-f { \fByang\fP | \fByin\fP | \fBtree\fP } ]
+.I FILE ...
+.br
+.B yanglint
+[\fIOPTIONS\fP]
+[\-f { \fBxml\fP | \fBjson\fP } ]
+\fISCHEMA\fP...
+\fIFILE\fP...
+.
+.SH DESCRIPTION
+\fByanglint\fP is a command-line tool for validating and converting YANG
+schemas and the YANG modeled data. For a simple use, it validates the provided
+file and if the output format specified, it converts input data into the output
+format. If started with no argument, \fByanglint\fP opens interactive
+environment where the user is allowed to work with schemas and data in a more
+complex way.
+.
+.SH OPTIONS
+.TP
+.BR "\-h\fR,\fP \-\^\-help"
+Outputs usage help and exits.
+.TP
+.BR "\-v\fR,\fP \-\^\-version"
+Outputs the version number and exits.
+.TP
+.BR "\-V\fR,\fP \-\^\-verbose"
+Increases the verbosity level. If not specified, only errors are printed, with
+each appearance it adds: warnings, verbose messages, debug messages (if compiled
+with debug information).
+.TP
+.BR "\-p \fIPATH\fP\fR,\fP \-\^\-path=\fIPATH\fP"
+Specifies search path for getting imported modules or included submodules. The option
+can be used multiple times. The current working directory and path of the module
+being added is used implicitly.
+.TP
+.BR "\-s\fR,\fP \-\^\-strict"
+Changes handling of unknown data nodes - instead of silently ignoring unknown data,
+error is printed and data parsing fails. This option applies only on data parsing.
+.TP
+.BR "\-f \fIFORMAT\fP\fR,\fP \-\^\-format=\fIFORMAT\fP"
+Converts the content of the input \fIFILE\fPs into the specified \fIFORMAT\fP. If no
+\fIOUTFILE\fP is specified, the data are printed on the standard output. Only the
+compatible formats for the input \fIFILE\fPs are allowed, see the section \fBFORMATS\fP.
+.TP
+.BR "\-o \fIOUTFILE\fP\fR,\fP \-\^\-output=\fIOUTFILE\fP"
+Writes the output data into the specified \fIOUTFILE\fP. The option can be used
+only in combination with \fB--format\fR option. In case of converting schema, only
+a single input schema \fIFILE\fP is allowed. In case of data input \fIFILE\fPs,
+input is merged and printed into a single \fIOUTFILE\fP.
+.TP
+.BR "\-F \fIFEATURES\fP\fR,\fP \-\^\-features=\fIFEATURES\fP"
+Specifies the list of enabled features in the format
+\fIMODULE\fP:[\fIFEATURE\fP,...]. In case of processing multiple modules, the
+option can be used repeatedly. To disable all the features, use an empty list
+specified for the particular module.
+.TP
+.BR "\-d \fIMODE\fP\fR,\fP \-\^\-default=\fIMODE\fP"
+Print data with default values, according to the \fIMODE\fP (to print attributes,
+the ietf-netconf-with-defaults model must be loaded). The \fIMODE\fP is one of the following:
+ \[bu] \fBall\fP             - add missing default nodes
+ \[bu] \fBall-tagged\fP      - add missing default nodes and mark all the default nodes with the attribute
+ \[bu] \fBtrim\fP            - remove all nodes with a default value
+ \[bu] \fBimplicit-tagged\fP - add missing nodes and mark them with the attribute
+.TP
+.BR "\-t \fITYPE\fP\fR,\fP \-\^\-type=\fITYPE\fP"
+Specify data tree type in the input data \fIFILE\fPs. The \fITYPE\fP is one of the following:
+ \[bu] \fBauto\fP            - Resolve data type (one of the following) automatically (as pyang does). Applicable only on XML input data.
+ \[bu] \fBdata\fP            - Complete datastore with status data (default type).
+ \[bu] \fBconfig\fP          - Configuration datastore (without status data).
+ \[bu] \fBget\fP             - Result of the NETCONF <get> operation.
+ \[bu] \fBgetconfig\fP       - Result of the NETCONF <get-config> operation.
+ \[bu] \fBedit\fP            - Content of the NETCONF <edit-config> operation.
+ \[bu] \fBrpc\fP             - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.
+ \[bu] \fBrpcreply\fP        - Reply to the RPC. This is just a virtual \fITYPE\fP, for parsing replies, '\fBauto\fP' must be used since the data \fIFILE\fPs are expected in pairs.
+.br
+                     The first input data \fIFILE\fP is expected as '\fBrpc\fP' \fITYPE\fP, the second \fIFILE\fP is expected as reply to the previous RPC.
+ \[bu] \fBnotif\fP           - Notification instance (content of the <notification> element without <eventTime>.
+.TP
+.BR "\-O \fIFILE\fP\fR,\fP \-\^\-operational=\fIFILE\fP]
+Optional parameter for '\fBrpc\fP' and '\fBnotif\fP' \fITYPE\fPs, the \fIFILE\fP contains running configuration datastore and
+state data referenced from the RPC/Notification. The same data apply to all input data \fIFILE\fPs. Note that the file
+is validated as '\fBdata\fP' \fITYPE\fP. Special value '\fB!\fP' can be used as \fIFILE\fP argument to ignore the external references.
+.TP
+.BR "\-y \fIYANGLIB_PATH\fP"
+Specify path to a yang-library data file (XML or JSON) describing the initial context.
+If provided, yanglint loads the modules according to the content of the yang-library data tree.
+Otherwise, an empty content with only the internal libyang modules is used. This does
+not limit user to load another modules explicitly specified as command line parameters.
+.
+.SH FORMATS
+There are two types of formats to use.
+.TP
+.I Schemas
+In case of schemas, the content can be converted into the '\fByang\fP', '\fByin\fP'
+and '\fBtree\fP' formats. As input, only YANG and YIN files are
+accepted. Note, that the corresponding file extension is required.
+.TP
+.I Data\ \ \
+In case of YANG modeled data, the content can be converted between '\fBxml\fP'
+and '\fBjson\fP' formats. Remember that the corresponding file extension of the
+input file is required.
+.
+
+.SH EXAMPLES
+.IP \[bu] 2
+Open interactive environment:
+    yanglint
+.IP \[bu]
+Convert YANG model into YIN and print it to the stdout:
+    yanglint --format=yin ./ietf-system.yang
+.IP \[bu]
+Convert ietf-system configuration data from XML to JSON:
+    yanglint --format=json --type=config --output=data.json ./ietf-system.yang ./data.xml
+
+.SH SEE ALSO
+https://github.com/CESNET/libyang (libyang homepage and Git repository)
+.
+.SH AUTHORS
+Radek Krejci <rkrejci@cesnet.cz>, Michal Vasko <mvasko@cesnet.cz>
+.
+.SH COPYRIGHT
+Copyright \(co 2015-2017 CESNET, a.l.e.
diff --git a/tools/re/main.c b/tools/re/main.c
new file mode 100644
index 0000000..96cc961
--- /dev/null
+++ b/tools/re/main.c
@@ -0,0 +1,313 @@
+/**
+ * @file main.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang's YANG Regular Expression tool
+ *
+ * Copyright (c) 2017 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 "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include "context.h"
+#include "plugins_types.h"
+
+void
+help(void)
+{
+    fprintf(stdout, "YANG Regular Expressions processor.\n");
+    fprintf(stdout, "Usage:\n");
+    fprintf(stdout, "    yangre [-hv]\n");
+    fprintf(stdout, "    yangre [-V] -p <regexp1> [-i] [-p <regexp2> [-i] ...] <string>\n");
+    fprintf(stdout, "    yangre [-V] -f <file>\n");
+    fprintf(stdout, "Returns 0 if string matches the pattern(s), 1 if not and -1 on error.\n\n");
+    fprintf(stdout, "Options:\n"
+        "  -h, --help              Show this help message and exit.\n"
+        "  -v, --version           Show version number and exit.\n"
+        "  -V, --verbose           Print the processing information.\n"
+        "  -i, --invert-match      Invert-match modifier for the closest preceding\n"
+        "                          pattern.\n"
+        "  -p, --pattern=\"REGEXP\"  Regular expression including the quoting,\n"
+        "                          which is applied the same way as in a YANG module.\n"
+        "  -f, --file=\"FILE\"     List of patterns and the <string> (separated by an\n"
+        "                          empty line) are taken from <file>. Invert-match is\n"
+        "                          indicated by the single space character at the \n"
+        "                          beginning of the pattern line. YANG quotation around\n"
+        "                          patterns is still expected, but that avoids issues with\n"
+        "                          reading quotation by shell. Avoid newline at the end\n"
+        "                          of the string line to represent empty <string>.");
+    fprintf(stdout, "Examples:\n"
+        "  pattern \"[0-9a-fA-F]*\";      -> yangre -p '\"[0-9a-fA-F]*\"' '1F'\n"
+        "  pattern '[a-zA-Z0-9\\-_.]*';  -> yangre -p \"'[a-zA-Z0-9\\-_.]*'\" 'a-b'\n"
+        "  pattern [xX][mM][lL].*;      -> yangre -p '[xX][mM][lL].*' 'xml-encoding'\n\n");
+    fprintf(stdout, "Note that to pass YANG quoting through your shell, you are supposed to use\n"
+                    "the other quotation around. For not-quoted patterns, use single quotes.\n\n");
+}
+
+void
+version(void)
+{
+    fprintf(stdout, "yangre %s\n", PROJECT_VERSION);
+}
+
+void
+pattern_error(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+    (void) path; /* unused */
+
+    if (level == LY_LLERR && strcmp(msg, "Module \"yangre\" parsing failed.")) {
+        fprintf(stderr, "yangre error: %s\n", msg);
+    }
+}
+
+static const char *module_start = "module yangre {"
+    "yang-version 1.1;"
+    "namespace urn:cesnet:libyang:yangre;"
+    "prefix re;"
+    "leaf pattern {"
+    "  type string {";
+static const char *module_invertmatch = " { modifier invert-match; }";
+static const char *module_match = ";";
+static const char *module_end = "}}}";
+
+static int
+add_pattern(char ***patterns, int **inverts, int *counter, char *pattern)
+{
+    void *reallocated1, *reallocated2;
+
+    (*counter)++;
+    reallocated1 = realloc(*patterns, *counter * sizeof **patterns);
+    reallocated2 = realloc(*inverts, *counter * sizeof **inverts);
+    if (!reallocated1 || !reallocated2) {
+        fprintf(stderr, "yangre error: memory allocation error.\n");
+        free(reallocated1);
+        free(reallocated2);
+        return EXIT_FAILURE;
+    }
+    (*patterns) = reallocated1;
+    (*patterns)[*counter - 1] = strdup(pattern);
+    (*inverts) = reallocated2;
+    (*inverts)[*counter - 1] = 0;
+
+    return EXIT_SUCCESS;
+}
+
+int
+main(int argc, char* argv[])
+{
+    LY_ERR match;
+    int i, opt_index = 0, ret = -1, verbose = 0, blankline = 0;
+    struct option options[] = {
+        {"help",             no_argument,       NULL, 'h'},
+        {"file",             required_argument, NULL, 'f'},
+        {"invert-match",     no_argument,       NULL, 'i'},
+        {"pattern",          required_argument, NULL, 'p'},
+        {"version",          no_argument,       NULL, 'v'},
+        {"verbose",          no_argument,       NULL, 'V'},
+        {NULL,               0,                 NULL, 0}
+    };
+    char **patterns = NULL, *str = NULL, *modstr = NULL, *s;
+    int *invert_match = NULL;
+    int patterns_count = 0;
+    struct ly_ctx *ctx = NULL;
+    const struct lys_module *mod;
+    FILE *infile = NULL;
+    size_t len = 0;
+    ssize_t l;
+    struct lysc_type *type;
+    struct ly_err_item *err;
+
+    opterr = 0;
+    while ((i = getopt_long(argc, argv, "hf:ivVp:", options, &opt_index)) != -1) {
+        switch (i) {
+        case 'h':
+            help();
+            ret = -2; /* continue to allow printing version and help at once */
+            break;
+        case 'f':
+            if (infile) {
+                help();
+                fprintf(stderr, "yangre error: multiple input files are not supported.\n");
+                goto cleanup;
+            } else if (patterns_count) {
+                help();
+                fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
+                goto cleanup;
+            }
+            infile = fopen(optarg, "r");
+            if (!infile) {
+                fprintf(stderr, "yangre error: unable to open input file %s (%s).\n", optarg, strerror(errno));
+                goto cleanup;
+            }
+
+            while((l = getline(&str, &len, infile)) != -1) {
+                if (!blankline && str[0] == '\n') {
+                    /* blank line */
+                    blankline = 1;
+                    continue;
+                }
+                if (str[0] != '\n' && str[l - 1] == '\n') {
+                    /* remove ending newline */
+                    str[l - 1] = '\0';
+                }
+                if (blankline) {
+                    /* done - str is now the string to check */
+                    blankline = 0;
+                    break;
+                    /* else read the patterns */
+                } else if (add_pattern(&patterns, &invert_match, &patterns_count,
+                                       str[0] == ' ' ? &str[1] : str)) {
+                    goto cleanup;
+                }
+                if (str[0] == ' ') {
+                    /* set invert-match */
+                    invert_match[patterns_count - 1] = 1;
+                }
+            }
+            if (blankline) {
+                /* corner case, no input after blankline meaning the pattern to check is empty */
+                if (str != NULL) {
+                    free(str);
+                }
+                str = malloc(sizeof(char));
+                str[0] = '\0';
+            }
+            break;
+        case 'i':
+            if (!patterns_count || invert_match[patterns_count - 1]) {
+                help();
+                fprintf(stderr, "yangre error: invert-match option must follow some pattern.\n");
+                goto cleanup;
+            }
+            invert_match[patterns_count - 1] = 1;
+            break;
+        case 'p':
+            if (infile) {
+                help();
+                fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
+                goto cleanup;
+            }
+            if (add_pattern(&patterns, &invert_match, &patterns_count, optarg)) {
+                goto cleanup;
+            }
+            break;
+        case 'v':
+            version();
+            ret = -2; /* continue to allow printing version and help at once */
+            break;
+        case 'V':
+            verbose = 1;
+            break;
+        default:
+            help();
+            if (optopt) {
+                fprintf(stderr, "yangre error: invalid option: -%c\n", optopt);
+            } else {
+                fprintf(stderr, "yangre error: invalid option: %s\n", argv[optind - 1]);
+            }
+            goto cleanup;
+        }
+    }
+
+    if (ret == -2) {
+        goto cleanup;
+    }
+
+    if (!str) {
+        /* check options compatibility */
+        if (optind >= argc) {
+            help();
+            fprintf(stderr, "yangre error: missing <string> parameter to process.\n");
+            goto cleanup;
+        } else if (!patterns_count) {
+            help();
+            fprintf(stderr, "yangre error: missing pattern parameter to use.\n");
+            goto cleanup;
+        }
+        str = argv[optind];
+    }
+
+    for (modstr = (char*)module_start, i = 0; i < patterns_count; i++) {
+        if (asprintf(&s, "%s pattern %s%s", modstr, patterns[i], invert_match[i] ? module_invertmatch : module_match) == -1) {
+            fprintf(stderr, "yangre error: memory allocation failed.\n");
+            goto cleanup;
+        }
+        if (modstr != module_start) {
+            free(modstr);
+        }
+        modstr = s;
+    }
+    if (asprintf(&s, "%s%s", modstr, module_end) == -1) {
+        fprintf(stderr, "yangre error: memory allocation failed.\n");
+        goto cleanup;
+    }
+    if (modstr != module_start) {
+        free(modstr);
+    }
+    modstr = s;
+
+    if (ly_ctx_new(NULL, 0, &ctx)) {
+        goto cleanup;
+    }
+
+    ly_set_log_clb(pattern_error, 0);
+    mod = lys_parse_mem(ctx, modstr, LYS_IN_YANG);
+    if (!mod || !mod->compiled || !mod->compiled->data) {
+        goto cleanup;
+    }
+
+    type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+    match = type->plugin->validate(ctx, type, str, strlen(str), LY_TYPE_OPTS_VALIDATE, NULL, &err, NULL);
+    if (verbose) {
+        for (i = 0; i < patterns_count; i++) {
+            fprintf(stdout, "pattern  %d: %s\n", i + 1, patterns[i]);
+            fprintf(stdout, "matching %d: %s\n", i + 1, invert_match[i] ? "inverted" : "regular");
+        }
+        fprintf(stdout, "string    : %s\n", str);
+        if (match == LY_SUCCESS) {
+            fprintf(stdout, "result    : matching\n");
+        } else if (match == LY_EVALID) {
+            fprintf(stdout, "result    : not matching\n");
+        } else {
+            fprintf(stdout, "result    : error (%s)\n", err->msg);
+        }
+    }
+    if (match == LY_SUCCESS) {
+        ret = 0;
+    } else if (match == LY_EVALID) {
+        ret = 1;
+    } else {
+        ret = -1;
+    }
+
+cleanup:
+    ly_ctx_destroy(ctx, NULL);
+    for (i = 0; i < patterns_count; i++) {
+        free(patterns[i]);
+    }
+    free(patterns);
+    free(invert_match);
+    free(modstr);
+    if (infile) {
+        fclose(infile);
+        free(str);
+    }
+    ly_err_free(err);
+
+    return ret;
+}
diff --git a/tools/re/yangre.1 b/tools/re/yangre.1
new file mode 100644
index 0000000..e7b572b
--- /dev/null
+++ b/tools/re/yangre.1
@@ -0,0 +1,118 @@
+.\" Manpage for yanglint.
+.\" Process this file with
+.\" groff -man -Tascii yangre.1
+.\"
+
+.TH YANGRE 1 "2018-11-09" "libyang"
+.SH NAME
+yangre \- YANG regular expression processor
+.
+.SH SYNOPSIS
+.B yangre
+[\-V] \-p \fIREGEXP\fP [\-i] [\-p \fIREGEXP\fP [\-i]...] \fISTRING\fP
+.br
+.B yangre
+[\-V] \-f \fIFILE\fP
+.
+.SH DESCRIPTION
+\fByangre\fP is a command-line tool to test and evaluate regular expressions
+for use in YANG schemas.  Supported regular expressions are defined by the
+W3C's XML-Schema standard.
+
+\fByangre\fP can be used either with regular expressions and a target string
+on the command line or with input from a file.  The latter is particularly
+useful to avoid dealing with proper shell escaping of regular expression
+patterns, which can be somewhat tricky.
+.
+.SH GENERAL OPTIONS
+.TP
+.BR "\-h\fR,\fP \-\^\-help"
+.br
+Outputs usage help and exits.
+.TP
+.BR "\-v\fR,\fP \-\^\-version"
+.br
+Outputs the version number and exits.
+.TP
+.BR "\-V\fR,\fP \-\^\-verbose"
+Increases the verbosity level. If not specified, only errors are printed, with
+each appearance it adds: warnings, verbose messages, debug messages (if compiled
+with debug information).
+.SH COMMAND LINE INPUT
+.TP
+.BR "\-p \fIREGEXP\fP\fR,\fP \-\^\-pattern=\fIREGEXP\fP"
+.br
+One or more regular expression patterns to be tested against the input
+string.  Supplied expressions are tested in the order they appear on the
+command line.  Testing is aborted when an expression does not match (or
+does match, if the \fB-i\fP option is used.)
+.TP
+.BR "\-i\fR,\fP \-\^\-invert-match"
+.br
+Reverse match condition for the previous pattern.  If the pattern matches,
+an error is printed and evaluation is aborted.
+.TP
+.BR "\fISTRING\fP"
+.br
+Target text input to match the regular expression(s) against.  The same
+text is used for all regular expressions.  Note that only the first
+argument is used by \fByangre\fP, if it contains spaces or other shell
+metacharacters they must be properly escaped.  Additional arguments are
+silently ignored.
+.SH FILE INPUT
+.TP
+.BR "\-f \fIFILE\fP\fR,\fP \-\^\-file=\fIFILE\fP"
+Read both patterns and target text from the specified input file.
+
+\fIFILE\fP must consist of one or more YANG regular expressions, each on
+their own line, followed by a blank line and one line of target text.  No
+preprocessing is done on file input, there are no comment lines and
+whitespace is not stripped.  A single space character at the beginning of
+a pattern line inverts the match condition for the pattern on that line.
+Patterns must still be properly quoted as mandated by the YANG standard.
+.SH RETURN VALUES
+.TP
+0
+.I Successful match
+.br
+The target text matched for all patterns.
+.TP
+1
+.I Pattern mismatch
+.br
+One or more patterns did not match the target text.  An error message is
+printed to stderr describing which pattern was the first not to match.
+.TP
+255
+.I Other error
+.br
+One or more patterns could not be processed or some other error occurred that
+precluded processing.
+.SH EXAMPLES
+.IP \[bu] 2
+Test a single pattern:
+    yangre -p 'te.*xt' text_text
+.IP \[bu]
+Test multiple patterns:
+    yangre -p '.*pat1' -p 'pat2.*' -p 'notpat' -i pat2testpat1
+.IP \[bu]
+Input from a file:
+    cat > /tmp/patterns <<EOF
+    .*pat1
+    pat2.*
+     notpat
+
+    pat2testpat1
+    EOF
+    yangre -f /tmp/patterns
+
+.SH SEE ALSO
+https://github.com/CESNET/libyang (libyang homepage and Git repository)
+.
+.SH AUTHORS
+Radek Krejci <rkrejci@cesnet.cz>, Michal Vasko <mvasko@cesnet.cz>
+.br
+This man page was written by David Lamparter <equinox@diac24.net>
+.
+.SH COPYRIGHT
+Copyright \(co 2015-2018 CESNET, a.l.e.