Merge pull request #948 from CESNET/print_info

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1bab1e9..875edc9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -213,6 +213,7 @@
     src/tree_data_hash.c
     src/parser_xml.c
     src/parser_json.c
+    src/printer.c
     src/printer_data.c
     src/printer_xml.c
     src/printer_json.c
@@ -249,6 +250,7 @@
     src/context.h
     src/tree.h
     src/tree_data.h
+    src/printer.h
     src/printer_data.h
     src/tree_schema.h
     src/printer_schema.h
@@ -331,6 +333,7 @@
     add_custom_target(doc
             COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile
             WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+    string(REPLACE ";" " " DOXY_HEADERS "${headers}")
     configure_file(Doxyfile.in Doxyfile)
 endif()
 
diff --git a/Doxyfile.in b/Doxyfile.in
index 869013f..9abd406 100644
--- a/Doxyfile.in
+++ b/Doxyfile.in
@@ -561,7 +561,7 @@
 # name. If set to NO, the members will appear in declaration order.
 # The default value is: YES.
 
-SORT_MEMBER_DOCS       = NO
+SORT_MEMBER_DOCS       = YES
 
 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
 # descriptions of file, namespace and class members alphabetically by member
@@ -569,7 +569,7 @@
 # this will also influence the order of the classes in the class list.
 # The default value is: NO.
 
-SORT_BRIEF_DOCS        = NO
+SORT_BRIEF_DOCS        = YES
 
 # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
 # (brief and detailed) documentation of class members so that constructors and
@@ -581,14 +581,14 @@
 # detailed member documentation.
 # The default value is: NO.
 
-SORT_MEMBERS_CTORS_1ST = NO
+SORT_MEMBERS_CTORS_1ST = YES
 
 # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
 # of group names into alphabetical order. If set to NO the group names will
 # appear in their defined order.
 # The default value is: NO.
 
-SORT_GROUP_NAMES       = NO
+SORT_GROUP_NAMES       = YES
 
 # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
 # fully-qualified names, including namespaces. If set to NO, the class list will
@@ -781,16 +781,7 @@
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = ./src/libyang.h \
-			 ./src/context.h \
-			 ./src/tree.h \
-                         ./src/tree_schema.h \
-                         ./src/plugins_types.h \
-                         ./src/plugins_exts.h \
-                         ./src/tree_data.h \
-                         ./src/log.h \
-                         ./src/set.h \
-                         ./src/dict.h
+INPUT                  = @DOXY_HEADERS@
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/src/libyang.h b/src/libyang.h
index eba8008..c86bd13 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -28,6 +28,8 @@
 #include "tree.h"
 #include "tree_data.h"
 #include "tree_schema.h"
+#include "printer.h"
+#include "printer_data.h"
 #include "printer_schema.h"
 #include "printer_data.h"
 #include "plugins_types.h"
diff --git a/src/printer.c b/src/printer.c
index e9a6041..6a70cdf 100644
--- a/src/printer.c
+++ b/src/printer.c
@@ -19,6 +19,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
 #include <unistd.h>
 #include <assert.h>
 
@@ -143,61 +144,371 @@
     return 1;
 }
 
-LY_ERR
-ly_print(struct lyout *out, const char *format, ...)
+API LYP_OUT_TYPE
+lyp_out_type(const struct lyp_out *out)
+{
+    LY_CHECK_ARG_RET(NULL, out, LYP_OUT_ERROR);
+    return out->type;
+}
+
+API struct lyp_out *
+lyp_new_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg)
+{
+    struct lyp_out *out;
+
+    out = calloc(1, sizeof *out);
+    LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
+
+    out->type = LYP_OUT_CALLBACK;
+    out->method.clb.func = writeclb;
+    out->method.clb.arg = arg;
+
+    return out;
+}
+
+API ssize_t (*lyp_clb(struct lyp_out *out, ssize_t (*writeclb)(void *arg, const void *buf, size_t count)))(void *arg, const void *buf, size_t count)
+{
+    void *prev_clb;
+
+    LY_CHECK_ARG_RET(NULL, out, out->type == LYP_OUT_CALLBACK, NULL);
+
+    prev_clb = out->method.clb.func;
+
+    if (writeclb) {
+        out->method.clb.func = writeclb;
+    }
+
+    return prev_clb;
+}
+
+API void *
+lyp_clb_arg(struct lyp_out *out, void *arg)
+{
+    void *prev_arg;
+
+    LY_CHECK_ARG_RET(NULL, out, out->type == LYP_OUT_CALLBACK, NULL);
+
+    prev_arg = out->method.clb.arg;
+
+    if (arg) {
+        out->method.clb.arg = arg;
+    }
+
+    return prev_arg;
+}
+
+API struct lyp_out *
+lyp_new_fd(int fd)
+{
+    struct lyp_out *out;
+
+    out = calloc(1, sizeof *out);
+    LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
+
+#ifdef HAVE_VDPRINTF
+    out->type = LYP_OUT_FD;
+    out->method.fd = fd;
+#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. */
+    out->type = LYP_OUT_FDSTREAM;
+    out->method.fdstream.fd = fd;
+
+    fd = dup(out->method.fdstream.fd);
+    if (fd < 0) {
+        LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
+               out->method.fdstream.fd, strerror(errno));
+        free(out);
+        return NULL;
+    }
+    out->method.fdstream.f = fdopen(fd, "a");
+    if (!out->method.fdstream.f) {
+        LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
+               out->method.fdstream.fd, strerror(errno));
+        free(out);
+        fclose(fd);
+        return NULL;
+    }
+#endif
+
+    return out;
+}
+
+API int
+lyp_fd(struct lyp_out *out, int fd)
+{
+    int prev_fd;
+
+    LY_CHECK_ARG_RET(NULL, out, out->type <= LYP_OUT_FDSTREAM, -1);
+
+    if (out->type == LYP_OUT_FDSTREAM) {
+        prev_fd = out->method.fdstream.fd;
+    } else { /* LYP_OUT_FD */
+        prev_fd = out->method.fd;
+    }
+
+    if (fd != -1) {
+        /* replace output stream */
+        if (out->type == LYP_OUT_FDSTREAM) {
+            int streamfd;
+            FILE *stream;
+
+            streamfd = dup(fd);
+            if (streamfd < 0) {
+                LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
+                return -1;
+            }
+            stream = fdopen(streamfd, "a");
+            if (!stream) {
+                LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
+                close(streamfd);
+                return -1;
+            }
+            /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
+            fclose(out->method.fdstream.f);
+            out->method.fdstream.f = stream;
+            out->method.fdstream.fd = streamfd;
+        } else { /* LYP_OUT_FD */
+            out->method.fd = fd;
+        }
+    }
+
+    return prev_fd;
+}
+
+API struct lyp_out *
+lyp_new_file(FILE *f)
+{
+    struct lyp_out *out;
+
+    out = calloc(1, sizeof *out);
+    LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
+
+    out->type = LYP_OUT_FILE;
+    out->method.f = f;
+
+    return out;
+}
+
+API FILE *
+lyp_file(struct lyp_out *out, FILE *f)
+{
+    FILE *prev_f;
+
+    LY_CHECK_ARG_RET(NULL, out, out->type == LYP_OUT_FILE, NULL);
+
+    prev_f = out->method.f;
+
+    if (f) {
+        out->method.f = f;
+    }
+
+    return prev_f;
+}
+
+API struct lyp_out *
+lyp_new_memory(char **strp, size_t size)
+{
+    struct lyp_out *out;
+
+    out = calloc(1, sizeof *out);
+    LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
+
+    out->type = LYP_OUT_MEMORY;
+    out->method.mem.buf = strp;
+    if (!size) {
+        /* buffer is supposed to be allocated */
+        *strp = NULL;
+    } else if (*strp) {
+        /* there is already buffer to use */
+        out->method.mem.size = size;
+    }
+
+    return out;
+}
+
+char *
+lyp_memory(struct lyp_out *out, char **strp, size_t size)
+{
+    char *data;
+
+    LY_CHECK_ARG_RET(NULL, out, out->type == LYP_OUT_MEMORY, NULL);
+
+    data = *out->method.mem.buf;
+
+    if (strp) {
+        out->method.mem.buf = strp;
+        out->method.mem.len = out->method.mem.size = 0;
+        out->printed = 0;
+        if (!size) {
+            /* buffer is supposed to be allocated */
+            *strp = NULL;
+        } else if (*strp) {
+            /* there is already buffer to use */
+            out->method.mem.size = size;
+        }
+    }
+
+    return data;
+}
+
+API LY_ERR
+lyp_out_reset(struct lyp_out *out)
+{
+    LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
+
+    switch(out->type) {
+    case LYP_OUT_ERROR:
+        LOGINT(NULL);
+        return LY_EINT;
+    case LYP_OUT_FD:
+        if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && errno != ESPIPE) {
+            LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
+            return LY_ESYS;
+        }
+        break;
+    case LYP_OUT_FDSTREAM:
+    case LYP_OUT_FILE:
+    case LYP_OUT_FILEPATH:
+        if ((fseek(out->method.f, 0, SEEK_SET) == -1) && errno != ESPIPE) {
+            LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
+            return LY_ESYS;
+        }
+        break;
+    case LYP_OUT_MEMORY:
+        out->printed = 0;
+        out->method.mem.len = 0;
+        break;
+    case LYP_OUT_CALLBACK:
+        /* nothing to do (not seekable) */
+        break;
+    }
+
+    return LY_SUCCESS;
+}
+
+API struct lyp_out *
+lyp_new_filepath(const char *filepath)
+{
+    struct lyp_out *out;
+
+    out = calloc(1, sizeof *out);
+    LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
+
+    out->type = LYP_OUT_FILEPATH;
+    out->method.fpath.f = fopen(filepath, "w");
+    if (!out->method.fpath.f) {
+        LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
+        return NULL;
+    }
+    out->method.fpath.filepath = strdup(filepath);
+    return out;
+}
+
+API const char *
+lyp_filepath(struct lyp_out *out, const char *filepath)
+{
+    FILE *f;
+
+    LY_CHECK_ARG_RET(NULL, out, out->type == LYP_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
+
+    if (!filepath) {
+        return out->method.fpath.filepath;
+    }
+
+    /* replace filepath */
+    f = out->method.fpath.f;
+    out->method.fpath.f = fopen(filepath, "w");
+    if (!out->method.fpath.f) {
+        LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
+        out->method.fpath.f = f;
+        return ((void *)-1);
+    }
+    fclose(f);
+    free(out->method.fpath.filepath);
+    out->method.fpath.filepath = strdup(filepath);
+
+    return NULL;
+}
+
+API void
+lyp_free(struct lyp_out *out, void (*clb_arg_destructor)(void *arg), int destroy)
+{
+    if (!out) {
+        return;
+    }
+
+    switch (out->type) {
+    case LYP_OUT_CALLBACK:
+        if (clb_arg_destructor) {
+            clb_arg_destructor(out->method.clb.arg);
+        }
+        break;
+    case LYP_OUT_FDSTREAM:
+        fclose(out->method.fdstream.f);
+        if (destroy) {
+            close(out->method.fdstream.fd);
+        }
+        break;
+    case LYP_OUT_FD:
+        if (destroy) {
+            close(out->method.fd);
+        }
+        break;
+    case LYP_OUT_FILE:
+        if (destroy) {
+            fclose(out->method.f);
+        }
+        break;
+    case LYP_OUT_MEMORY:
+        if (destroy) {
+            free(*out->method.mem.buf);
+        }
+        break;
+    case LYP_OUT_FILEPATH:
+        free(out->method.fpath.filepath);
+        if (destroy) {
+            fclose(out->method.fpath.f);
+        }
+        break;
+    case LYP_OUT_ERROR:
+        LOGINT(NULL);
+    }
+    free(out);
+}
+
+API LY_ERR
+lyp_print(struct lyp_out *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:
+    case LYP_OUT_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;
+        /* never should be here since lyp_fd() is supposed to set type to LYP_OUT_FDSTREAM in case vdprintf() is missing */
+        LOGINT(NULL);
+        return LY_EINT;
 #endif
-        /* fall through */
-    case LYOUT_FDSTREAM:
-    case LYOUT_STREAM:
+    case LYP_OUT_FDSTREAM:
+    case LYP_OUT_FILEPATH:
+    case LYP_OUT_FILE:
         count = vfprintf(out->method.f, format, ap);
         break;
-    case LYOUT_MEMORY:
+    case LYP_OUT_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);
+            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;
@@ -206,21 +517,23 @@
                 va_end(ap);
                 return LY_EMEM;
             }
-            out->method.mem.buf = aux;
+            *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);
+        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';
+        (*out->method.mem.buf)[out->method.mem.len] = '\0';
         free(msg);
         break;
-    case LYOUT_CALLBACK:
+    case LYP_OUT_CALLBACK:
         if ((count = vasprintf(&msg, format, ap)) < 0) {
             break;
         }
-        count = out->method.clb.f(out->method.clb.arg, msg, count);
+        count = out->method.clb.func(out->method.clb.arg, msg, count);
         free(msg);
         break;
+    case LYP_OUT_ERROR:
+        LOGINT(NULL);
     }
 
     va_end(ap);
@@ -230,34 +543,45 @@
         out->status = LY_ESYS;
         return LY_ESYS;
     } else {
+        if (out->type == LYP_OUT_FDSTREAM) {
+            /* move the original file descriptor to the end of the output file */
+            lseek(out->method.fdstream.fd, 0, SEEK_END);
+        }
         out->printed += count;
         return LY_SUCCESS;
     }
 }
 
 void
-ly_print_flush(struct lyout *out)
+ly_print_flush(struct lyp_out *out)
 {
     switch (out->type) {
-    case LYOUT_FDSTREAM:
-    case LYOUT_STREAM:
+    case LYP_OUT_FDSTREAM:
+        /* move the original file descriptor to the end of the output file */
+        lseek(out->method.fdstream.fd, 0, SEEK_END);
+        fflush(out->method.fdstream.f);
+        break;
+    case LYP_OUT_FILEPATH:
+    case LYP_OUT_FILE:
         fflush(out->method.f);
         break;
-    case LYOUT_FD:
+    case LYP_OUT_FD:
         fsync(out->method.fd);
         break;
-    case LYOUT_MEMORY:
-    case LYOUT_CALLBACK:
+    case LYP_OUT_MEMORY:
+    case LYP_OUT_CALLBACK:
         /* nothing to do */
         break;
+    case LYP_OUT_ERROR:
+        LOGINT(NULL);
     }
 
     free(out->buffered);
     out->buf_size = out->buf_len = 0;
 }
 
-LY_ERR
-ly_write(struct lyout *out, const char *buf, size_t len)
+API LY_ERR
+lyp_write(struct lyp_out *out, const char *buf, size_t len)
 {
     int written = 0;
 
@@ -282,32 +606,35 @@
 
 repeat:
     switch (out->type) {
-    case LYOUT_MEMORY:
+    case LYP_OUT_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.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);
+        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->method.mem.buf)[out->method.mem.len] = '\0';
 
         out->printed += len;
         return LY_SUCCESS;
-    case LYOUT_FD:
+    case LYP_OUT_FD:
         written = write(out->method.fd, buf, len);
         break;
-    case LYOUT_FDSTREAM:
-    case LYOUT_STREAM:
+    case LYP_OUT_FDSTREAM:
+    case LYP_OUT_FILEPATH:
+    case LYP_OUT_FILE:
         written =  fwrite(buf, sizeof *buf, len, out->method.f);
         break;
-    case LYOUT_CALLBACK:
-        written = out->method.clb.f(out->method.clb.arg, buf, len);
+    case LYP_OUT_CALLBACK:
+        written = out->method.clb.func(out->method.clb.arg, buf, len);
         break;
+    case LYP_OUT_ERROR:
+        LOGINT(NULL);
     }
 
     if (written < 0) {
@@ -322,21 +649,25 @@
         out->status = LY_ESYS;
         return LY_ESYS;
     } else {
+        if (out->type == LYP_OUT_FDSTREAM) {
+            /* move the original file descriptor to the end of the output file */
+            lseek(out->method.fdstream.fd, 0, SEEK_END);
+        }
         out->printed += written;
         return LY_SUCCESS;
     }
 }
 
 LY_ERR
-ly_write_skip(struct lyout *out, size_t count, size_t *position)
+ly_write_skip(struct lyp_out *out, size_t count, size_t *position)
 {
     LYOUT_CHECK(out, out->status);
 
     switch (out->type) {
-    case LYOUT_MEMORY:
+    case LYP_OUT_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.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;
@@ -354,10 +685,11 @@
         /* 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:
+    case LYP_OUT_FD:
+    case LYP_OUT_FDSTREAM:
+    case LYP_OUT_FILEPATH:
+    case LYP_OUT_FILE:
+    case LYP_OUT_CALLBACK:
         /* buffer the hole */
         if (out->buf_len + count > out->buf_size) {
             out->buffered = ly_realloc(out->buffered, out->buf_len + count);
@@ -378,27 +710,32 @@
 
         /* increase hole counter */
         ++out->hole_count;
+
+        break;
+    case LYP_OUT_ERROR:
+        LOGINT(NULL);
     }
 
     return LY_SUCCESS;
 }
 
 LY_ERR
-ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
+ly_write_skipped(struct lyp_out *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:
+    case LYP_OUT_MEMORY:
         /* write */
-        memcpy(&out->method.mem.buf[position], buf, count);
+        memcpy(&(*out->method.mem.buf)[position], buf, count);
         break;
-    case LYOUT_FD:
-    case LYOUT_FDSTREAM:
-    case LYOUT_STREAM:
-    case LYOUT_CALLBACK:
+    case LYP_OUT_FD:
+    case LYP_OUT_FDSTREAM:
+    case LYP_OUT_FILEPATH:
+    case LYP_OUT_FILE:
+    case LYP_OUT_CALLBACK:
         if (out->buf_len < position + count) {
             out->status = LY_ESYS;
             LOGMEM_RET(NULL);
@@ -413,11 +750,17 @@
         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);
+            ret = lyp_write(out, out->buffered, out->buf_len);
             out->buf_len = 0;
         }
         break;
+    case LYP_OUT_ERROR:
+        LOGINT(NULL);
     }
 
+    if (out->type == LYP_OUT_FILEPATH) {
+        /* move the original file descriptor to the end of the output file */
+        lseek(out->method.fdstream.fd, 0, SEEK_END);
+    }
     return ret;
 }
diff --git a/src/printer.h b/src/printer.h
new file mode 100644
index 0000000..0034132
--- /dev/null
+++ b/src/printer.h
@@ -0,0 +1,208 @@
+/**
+ * @file printer.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic libyang printer structures and 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
+ */
+
+#ifndef LY_PRINTER_H_
+#define LY_PRINTER_H_
+
+#include <unistd.h>
+
+/**
+ * @brief Printer output structure specifying where the data are printed.
+ */
+struct lyp_out;
+
+/**
+ * @brief Types of the printer's output
+ */
+typedef enum LYP_OUT_TYPE {
+    LYP_OUT_ERROR = -1,  /**< error value to indicate failure of the functions returning LYP_OUT_TYPE */
+    LYP_OUT_FD,          /**< file descriptor printer */
+    LYP_OUT_FDSTREAM,    /**< internal replacement for LYP_OUT_FD in case vdprintf() is not available */
+    LYP_OUT_FILE,        /**< FILE stream printer */
+    LYP_OUT_FILEPATH,    /**< filepath printer */
+    LYP_OUT_MEMORY,      /**< memory printer */
+    LYP_OUT_CALLBACK     /**< callback printer */
+} LYP_OUT_TYPE;
+
+/**
+ * @brief Get output type of the printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @return Type of the printer's output.
+ */
+LYP_OUT_TYPE lyp_out_type(const struct lyp_out *out);
+
+/**
+ * @brief Reset the output medium to write from its beginning, so the following printer function will rewrite the current data
+ * instead of appending.
+ *
+ * Note that in case the underlying output is not seekable (stream referring a pipe/FIFO/socket or the callback output type),
+ * nothing actually happens despite the function succeeds. Also note that the medium is not returned to the state it was when
+ * the handler was created. For example, file is seeked into the offset zero, not to the offset where it was opened when
+ * lyp_new_file() was called.
+ *
+ * @param[in] out Printer handler.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ESYS in case of failure
+ */
+LY_ERR lyp_out_reset(struct lyp_out *out);
+
+/**
+ * @brief Create printer handler using callback printer function.
+ *
+ * @param[in] writeclb Pointer to the printer callback function writing the data (see write(2)).
+ * @param[in] arg Optional caller-specific argument to be passed to the @p writeclb callback.
+ * @return NULL in case of memory allocation error.
+ * @return Created printer handler supposed to be passed to different ly*_print_*() functions.
+ */
+struct lyp_out *lyp_new_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg);
+
+/**
+ * @brief Get or reset callback function associated with a callback printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] fd Optional value of a new file descriptor for the handler. If -1, only the current file descriptor value is returned.
+ * @return Previous value of the file descriptor.
+ */
+ssize_t (*lyp_clb(struct lyp_out *out, ssize_t (*writeclb)(void *arg, const void *buf, size_t count)))(void *arg, const void *buf, size_t count);
+
+/**
+ * @brief Get or reset callback function's argument aasociated with a callback printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] arg caller-specific argument to be passed to the callback function associated with the printer handler.
+ * If NULL, only the current file descriptor value is returned.
+ * @return The previous callback argument.
+ */
+void *lyp_clb_arg(struct lyp_out *out, void *arg);
+
+/**
+ * @brief Create printer handler using file descriptor.
+ *
+ * @param[in] fd File descriptor to use.
+ * @return NULL in case of error.
+ * @return Created printer handler supposed to be passed to different ly*_print_*() functions.
+ */
+struct lyp_out *lyp_new_fd(int fd);
+
+/**
+ * @brief Get or reset file descriptor printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] fd Optional value of a new file descriptor for the handler. If -1, only the current file descriptor value is returned.
+ * @return Previous value of the file descriptor. Note that caller is responsible for closing the returned file descriptor in case of setting new descriptor @p fd.
+ * @return -1 in case of error when setting up the new file descriptor.
+ */
+int lyp_fd(struct lyp_out *out, int fd);
+
+/**
+ * @brief Create printer handler using file stream.
+ *
+ * @param[in] f File stream to use.
+ * @return NULL in case of error.
+ * @return Created printer handler supposed to be passed to different ly*_print_*() functions.
+ */
+struct lyp_out *lyp_new_file(FILE *f);
+
+/**
+ * @brief Get or reset file stream printer handler.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] f Optional new file stream for the handler. If NULL, only the current file stream is returned.
+ * @return Previous file stream of the handler. Note that caller is responsible for closing the returned stream in case of setting new stream @p f.
+ */
+FILE *lyp_file(struct lyp_out *out, FILE *f);
+
+/**
+ * @brief Create printer handler using memory to dump data.
+ *
+ * @param[in] strp Pointer to store the resulting data. If it points to a pointer to an allocated buffer and
+ * @p size of the buffer is set, the buffer is used (and extended if needed) to store the printed data.
+ * @param[in] size Size of the buffer provided via @p strp. In case it is 0, the buffer for the printed data
+ * is newly allocated even if @p strp points to a pointer to an existing buffer.
+ * @return NULL in case of error.
+ * @return Created printer handler supposed to be passed to different ly*_print_*() functions.
+ */
+struct lyp_out *lyp_new_memory(char **strp, size_t size);
+
+/**
+ * @brief Get or change memory where the data are dumped.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] strp A new string pointer to store the resulting data, same rules as in lyp_new_memory() are applied.
+ * @param[in] size Size of the buffer provided via @p strp. In case it is 0, the buffer for the printed data
+ * is newly allocated even if @p strp points to a pointer to an existing buffer.
+ * @return Previous dumped data. Note that the caller is responsible to free the data in case of changing string pointer @p strp.
+ */
+char *lyp_memory(struct lyp_out *out, char **strp, size_t size);
+
+/**
+ * @brief Create printer handler file of the given filename.
+ *
+ * @param[in] filepath Path of the file where to write data.
+ * @return NULL in case of error.
+ * @return Created printer handler supposed to be passed to different ly*_print_*() functions.
+ */
+struct lyp_out *lyp_new_filepath(const char *filepath);
+
+/**
+ * @brief Get or change the filepath of the file where the printer prints the data.
+ *
+ * Note that in case of changing the filepath, the current file is closed and a new one is
+ * created/opened instead of renaming the previous file. Also note that the previous filepath
+ * string is returned only in case of not changing it's value.
+ *
+ * @param[in] out Printer handler.
+ * @param[in] filepath Optional new filepath for the handler. If and only if NULL, the current filepath string is returned.
+ * @return Previous filepath string in case the @p filepath argument is NULL.
+ * @return NULL if changing filepath succeedes and ((void *)-1) otherwise.
+ */
+const char *lyp_filepath(struct lyp_out *out, const char *filepath);
+
+/**
+ * @brief Generic printer of the given format string into the specified output.
+ *
+ * Alternatively, lyp_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 lyp_print(struct lyp_out *out, const char *format, ...);
+
+/**
+ * @brief Generic printer of the given string buffer into the specified output.
+ *
+ * Alternatively, lyp_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 lyp_write(struct lyp_out *out, const char *buf, size_t len);
+
+/**
+ * @brief Free the printer handler.
+ * @param[in] out Printer handler to free.
+ * @param[in] clb_arg_destructor Freeing function for printer callback (LYP_OUT_CALLBACK) argument.
+ * @param[in] destroy Flag to free allocated buffer (for LYP_OUT_MEMORY) or to
+ * close stream/file descriptor (for LYP_OUT_FD, LYP_OUT_FDSTREAM and LYP_OUT_FILE)
+ */
+void lyp_free(struct lyp_out *out, void (*clb_arg_destructor)(void *arg), int destroy);
+
+#endif /* LY_PRINTER_H_ */
diff --git a/src/printer_data.c b/src/printer_data.c
index 790b19d..96c2109 100644
--- a/src/printer_data.c
+++ b/src/printer_data.c
@@ -21,6 +21,7 @@
 
 #include "log.h"
 #include "printer_internal.h"
+#include "printer_data.h"
 #include "tree_schema.h"
 #include "tree_data.h"
 
@@ -33,10 +34,15 @@
  * @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)
+API ssize_t
+lyd_print(struct lyp_out *out, const struct lyd_node *root, LYD_FORMAT format, int options)
 {
     LY_ERR ret;
+    size_t printed_prev;
+
+    LY_CHECK_ARG_RET(NULL, out, root, -LY_EINVAL);
+
+    printed_prev = out->printed;
 
     switch (format) {
     case LYD_XML:
@@ -56,139 +62,11 @@
         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 = LYD_NODE_CTX(root);
-    }
-
-    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 ? LYD_NODE_CTX(root) : 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 = LYD_NODE_CTX(root);
-    }
-
-    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 = LYD_NODE_CTX(root);
-    }
-
-    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 = LYD_NODE_CTX(root);
-    }
-
-    ret = lyd_print_(&out, root, format, options);
-    if (ret) {
-        /* error */
-        return (-1) * ret;
-    } else {
-        /* success */
-        return (ssize_t)out.printed;
+        return (ssize_t)(out->printed - printed_prev);
     }
 }
diff --git a/src/printer_data.h b/src/printer_data.h
index 38c757b..921793f 100644
--- a/src/printer_data.h
+++ b/src/printer_data.h
@@ -19,6 +19,7 @@
 #include <unistd.h>
 
 #include "tree_data.h"
+#include "printer.h"
 
 /**
  * @defgroup dataprinterflags Data printer flags
@@ -50,71 +51,15 @@
  */
 
 /**
- * @brief Print data tree in the specified format into a memory block.
- * It is up to caller to free the returned string by free().
+ * @brief Common YANG data printer.
  *
- * @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] out Printer handler for a specific output. Use lyp_*() functions to create the handler and lyp_free() to remove the handler.
+ * @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 Number of printed characters (excluding the null byte used to end the string) 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 characters (excluding the null byte used to end the string) 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 characters (excluding the null byte used to end the string) 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 characters (excluding the null byte used to end the string) 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 characters (excluding the null byte used to end the string) 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);
+ssize_t lyd_print(struct lyp_out *out, 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
index 139a5bc..f60f6d4 100644
--- a/src/printer_internal.h
+++ b/src/printer_internal.h
@@ -15,37 +15,35 @@
 #ifndef LY_PRINTER_INTERNAL_H_
 #define LY_PRINTER_INTERNAL_H_
 
+#include "printer.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 */
+struct lyp_out {
+    LYP_OUT_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 */
+        int fd;          /**< file descriptor for LYP_OUT_FD type */
+        FILE *f;         /**< file structure for LYP_OUT_STREAM, LYP_OUT_FDSTREAM and LYP_OUT_FILEPATH types */
         struct {
-            char *buf;        /**< pointer to the memory buffer to store the output */
+            FILE *f;          /**< file stream from the original file descriptor, variable is mapped to the LYP_OUT_STREAM's f */
+            int fd;           /**< original file descriptor, which was not used directly because of missing vdprintf() */
+        } fdstream;      /**< structure for LYP_OUT_FDSTREAM type, which is LYP_OUT_FD when vdprintf() is missing */
+        struct {
+            FILE *f;          /**< file structure for LYP_OUT_FILEPATH, variable is mapped to the LYP_OUT_STREAM's f */
+            char *filepath;   /**< stored original filepath */
+        } fpath;         /**< filepath structure for LYP_OUT_FILEPATH */
+        struct {
+            char **buf;       /**< storage for the 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 */
+        } mem;           /**< memory buffer information for LYP_OUT_MEMORY type */
         struct {
-            ssize_t (*f)(void *arg, const void *buf, size_t count); /**< callback function */
+            ssize_t (*func)(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 */
+        } clb;           /**< printer callback for LYP_OUT_CALLBACK type */
     } method;            /**< type-specific information about the output */
 
     char *buffered;      /**< additional buffer for holes, used only for LYB data format */
@@ -85,7 +83,7 @@
  * @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);
+LY_ERR yang_print_parsed(struct lyp_out *out, const struct lys_module *module);
 
 /**
  * @brief YANG printer of the compiled schemas.
@@ -96,9 +94,24 @@
  *
  * @param[in] out Output specification.
  * @param[in] module Schema to be printed (the compiled member is used).
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
  * @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);
+LY_ERR yang_print_compiled(struct lyp_out *out, const struct lys_module *module, int options);
+
+/**
+ * @brief YANG printer of the compiled schema node
+ *
+ * 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] node Schema node to be printed including all its substatements.
+ * @param[in] options Schema output options (see @ref schemaprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in lyout::printed.
+ */
+LY_ERR yang_print_compiled_node(struct lyp_out *out, const struct lysc_node *node, int options);
 
 /**
  * @brief YIN printer of the parsed schemas. Full YIN printer.
@@ -107,7 +120,7 @@
  * @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 yin_print_parsed(struct lyout *out, const struct lys_module *module);
+LY_ERR yin_print_parsed(struct lyp_out *out, const struct lys_module *module);
 
 /**
  * @brief XML printer of the YANG data.
@@ -117,7 +130,7 @@
  * @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);
+LY_ERR xml_print_data(struct lyp_out *out, const struct lyd_node *root, int options);
 
 /**
  * @brief Check whether a node value equals to its default one.
@@ -139,36 +152,10 @@
 int ly_should_print(const struct lyd_node *node, 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);
+void ly_print_flush(struct lyp_out *out);
 
 /**
  * @brief Create a hole in the output data that will be filled later.
@@ -179,7 +166,7 @@
  * @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);
+LY_ERR ly_write_skip(struct lyp_out *out, size_t len, size_t *position);
 
 /**
  * @brief Write data into the hole at given position.
@@ -192,6 +179,6 @@
  * @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);
+LY_ERR ly_write_skipped(struct lyp_out *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
index bacf136..a4cbf1f 100644
--- a/src/printer_schema.c
+++ b/src/printer_schema.c
@@ -20,30 +20,26 @@
 #include <unistd.h>
 
 #include "log.h"
+#include "printer.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))
+API ssize_t
+lys_print(struct lyp_out *out, const struct lys_module *module, LYS_OUTFORMAT format, int UNUSED(line_length), int options)
 {
     LY_ERR ret;
+    size_t printed_prev;
+
+    LY_CHECK_ARG_RET(NULL, out, module, -LY_EINVAL);
+
+    printed_prev = out->printed;
 
     switch (format) {
     case LYS_OUT_YANG:
         ret = yang_print_parsed(out, module);
         break;
     case LYS_OUT_YANG_COMPILED:
-        ret = yang_print_compiled(out, module);
+        ret = yang_print_compiled(out, module, options);
         break;
     case LYS_OUT_YIN:
         ret = yin_print_parsed(out, module);
@@ -55,81 +51,48 @@
     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.");
+        LOGERR(module->ctx, LY_EINVAL, "Unsupported 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;
+        return (ssize_t)(out->printed - printed_prev);
     }
 }
 
 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)
+lys_print_node(struct lyp_out *out, const struct lysc_node *node, LYS_OUTFORMAT format, int UNUSED(line_length), int options)
 {
     LY_ERR ret;
-    struct lyout out;
+    size_t printed_prev;
 
-    LY_CHECK_ARG_RET(NULL, fd >= 0, module, LY_EINVAL);
+    LY_CHECK_ARG_RET(NULL, out, node, -LY_EINVAL);
 
-    memset(&out, 0, sizeof out);
-    out.ctx = module->ctx;
-    out.type = LYOUT_FD;
-    out.method.fd = fd;
+    printed_prev = out->printed;
 
-    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);
+    switch (format) {
+    case LYS_OUT_YANG_COMPILED:
+        ret = yang_print_compiled_node(out, node, options);
+        break;
+    /* TODO not yet implemented
+    case LYS_OUT_YIN:
+        ret = yin_print_parsed(out, module);
+        break;
+    case LYS_OUT_TREE:
+        ret = tree_print_model(out, module, target_node, line_length, options);
+        break;
+    */
+    default:
+        LOGERR(NULL, LY_EINVAL, "Unsupported output format.");
+        ret = LY_EINVAL;
+        break;
     }
 
     if (ret) {
@@ -137,55 +100,7 @@
         return (-1) * ret;
     } else {
         /* success */
-        return (ssize_t)out.printed;
+        return (ssize_t)(out->printed - printed_prev);
     }
 }
 
-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
index 613e768..1162cc2 100644
--- a/src/printer_schema.h
+++ b/src/printer_schema.h
@@ -18,74 +18,53 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#include "printer.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().
+ * @addtogroup schematree
+ * @{
+ */
+
+/**
+ * @defgroup schemaprinterflags Schema output options
+ * @{
+ */
+#define LYS_OUTPUT_NO_SUBSTMT        0x10 /**< Print only top-level/referede node information,
+                                               do not print information from the substatements */
+//#define LYS_OUTOPT_TREE_RFC        0x01 /**< Conform to the RFC TODO tree output (only for tree format) */
+//#define LYS_OUTOPT_TREE_GROUPING   0x02 /**< Print groupings separately (only for tree format) */
+//#define LYS_OUTOPT_TREE_USES       0x04 /**< Print only uses instead the resolved grouping nodes (only for tree format) */
+//#define LYS_OUTOPT_TREE_NO_LEAFREF 0x08 /**< Do not print the target of leafrefs (only for tree format) */
+
+/** @} schemaprinterflags */
+
+/**
+ * @brief Schema module printer.
  *
- * @param[out] strp Pointer to store the resulting dump.
- * @param[in] module Schema tree to print.
- * @param[in] format Schema output format.
+ * @param[in] out Printer handler for a specific output. Use lyp_*() functions to create the handler and lyp_free() to remove the handler.
+ * @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 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);
+ssize_t lys_print(struct lyp_out *out, 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.
+ * @brief Schema node printer.
  *
- * @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] out Printer handler for a specific output. Use lyp_*() functions to create the handler and lyp_free() to remove the handler.
+ * @param[in] node Schema node to print, lys_find_node() can be used to get it from a path string.
+ * @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 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);
+ssize_t lys_print_node(struct lyp_out *out, const struct lysc_node *node, 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);
+/** @} schematree */
 
 #endif /* LY_PRINTER_SCHEMA_H_ */
diff --git a/src/printer_xml.c b/src/printer_xml.c
index 279d336..b1d187c 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -32,7 +32,7 @@
  * @brief XML printer context.
  */
 struct xmlpr_ctx {
-    struct lyout *out;  /**< output specification */
+    struct lyp_out *out;  /**< output specification */
     unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
     int options;        /**< [Data printer flags](@ref dataprinterflags) */
     const struct ly_ctx *ctx; /**< libyang context */
@@ -89,7 +89,7 @@
 
     if (i == -1) {
         /* suitable namespace not found, must be printed */
-        ly_print(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
+        lyp_print(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
 
         /* and added into namespaces */
         if (new_prefix) {
@@ -144,7 +144,7 @@
             /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
             mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
             if (mod) {
-                ly_print(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
+                lyp_print(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
             }
         }
     }
@@ -177,17 +177,17 @@
                 }
 
                 for (i = 0; i < ns_count; ++i) {
-                    ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
+                    lyp_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
                 }
                 free(prefs);
                 free(nss);
             }
-            ly_print(out, " %s=\"", meta->name);
+            lyp_print(out, " %s=\"", meta->name);
         } else {
 #endif
             /* print the metadata with its namespace */
             mod = meta->annotation->module;
-            ly_print(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
+            lyp_print(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
 #if 0
         }
 #endif
@@ -196,7 +196,7 @@
         if (value && value[0]) {
             lyxml_dump_text(ctx->out, value, 1);
         }
-        ly_print(ctx->out, "\"");
+        lyp_print(ctx->out, "\"");
         if (dynamic) {
             free((void *)value);
         }
@@ -215,7 +215,7 @@
 xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
 {
     /* print node name */
-    ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
+    lyp_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
 
     /* print default namespace */
     xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
@@ -254,7 +254,7 @@
         }
 
         /* print the attribute with its prefix and value */
-        ly_print(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
+        lyp_print(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
     }
 
     return LY_SUCCESS;
@@ -264,7 +264,7 @@
 xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
 {
     /* print node name */
-    ly_print(ctx->out, "%*s<%s", INDENT, node->name);
+    lyp_print(ctx->out, "%*s<%s", INDENT, node->name);
 
     /* print default namespace */
     switch (node->format) {
@@ -305,16 +305,16 @@
     /* print namespaces connected with the values's prefixes */
     for (u = 0; u < ns_list.count; ++u) {
         const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
-        ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
+        lyp_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
     }
     ly_set_erase(&ns_list, NULL);
 
     if (!value || !value[0]) {
-        ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
+        lyp_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
     } else {
-        ly_print(ctx->out, ">");
+        lyp_print(ctx->out, ">");
         lyxml_dump_text(ctx->out, value, 0);
-        ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
+        lyp_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
     }
     if (dynamic) {
         free((void *)value);
@@ -337,12 +337,12 @@
     xml_print_node_open(ctx, (struct lyd_node *)node);
 
     if (!node->child) {
-        ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
+        lyp_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
         return LY_SUCCESS;
     }
 
     /* children */
-    ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
+    lyp_print(ctx->out, ">%s", ctx->level ? "\n" : "");
 
     LEVEL_INC;
     LY_LIST_FOR(node->child, child) {
@@ -351,7 +351,7 @@
     }
     LEVEL_DEC;
 
-    ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
+    lyp_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
 
     return LY_SUCCESS;
 }
@@ -369,7 +369,7 @@
     if (!any->value.tree) {
         /* no content */
 no_content:
-        ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
+        lyp_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
         return LY_SUCCESS;
     } else {
         switch (any->value_type) {
@@ -379,7 +379,7 @@
             ctx->options &= ~LYDP_WITHSIBLINGS;
             LEVEL_INC;
 
-            ly_print(ctx->out, ">%s", LEVEL ? "\n" : "");
+            lyp_print(ctx->out, ">%s", LEVEL ? "\n" : "");
             LY_LIST_FOR(any->value.tree, iter) {
                 ret = xml_print_node(ctx, iter);
                 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
@@ -394,7 +394,7 @@
                 goto no_content;
             }
             /* close opening tag and print data */
-            ly_print(ctx->out, ">");
+            lyp_print(ctx->out, ">");
             lyxml_dump_text(ctx->out, any->value.str, 0);
             break;
         case LYD_ANYDATA_XML:
@@ -402,7 +402,7 @@
             if (!any->value.str[0]) {
                 goto no_content;
             }
-            ly_print(ctx->out, ">%s", any->value.str);
+            lyp_print(ctx->out, ">%s", any->value.str);
             break;
         case LYD_ANYDATA_JSON:
 #if 0 /* TODO LYB format */
@@ -415,9 +415,9 @@
 
         /* closing tag */
         if (any->value_type == LYD_ANYDATA_DATATREE) {
-            ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
+            lyp_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
         } else {
-            ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
+            lyp_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
         }
     }
 
@@ -441,13 +441,13 @@
             }
         }
 
-        ly_print(ctx->out, ">%s", node->value);
+        lyp_print(ctx->out, ">%s", node->value);
     }
 
     if (node->child) {
         /* children */
         if (!node->value[0]) {
-            ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
+            lyp_print(ctx->out, ">%s", ctx->level ? "\n" : "");
         }
 
         LEVEL_INC;
@@ -457,12 +457,12 @@
         }
         LEVEL_DEC;
 
-        ly_print(ctx->out, "%*s</%s>%s", INDENT, node->name, LEVEL ? "\n" : "");
+        lyp_print(ctx->out, "%*s</%s>%s", INDENT, node->name, LEVEL ? "\n" : "");
     } else if (node->value[0]) {
-        ly_print(ctx->out, "</%s>%s", node->name, LEVEL ? "\n" : "");
+        lyp_print(ctx->out, "</%s>%s", node->name, LEVEL ? "\n" : "");
     } else {
         /* no value or children */
-        ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
+        lyp_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
     }
 
     return LY_SUCCESS;
@@ -526,14 +526,14 @@
 }
 
 LY_ERR
-xml_print_data(struct lyout *out, const struct lyd_node *root, int options)
+xml_print_data(struct lyp_out *out, const struct lyd_node *root, int options)
 {
     const struct lyd_node *node;
     struct xmlpr_ctx ctx = {0};
 
     if (!root) {
-        if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
-            ly_print(out, "");
+        if (out->type == LYP_OUT_MEMORY || out->type == LYP_OUT_CALLBACK) {
+            lyp_print(out, "");
         }
         goto finish;
     }
diff --git a/src/printer_yang.c b/src/printer_yang.c
index 41fb7e9..43d3833 100755
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -41,10 +41,11 @@
  * @brief YANG printer context.
  */
 struct ypr_ctx {
-    struct lyout *out;               /**< output specification */
+    struct lyp_out *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 */
+    int options;                     /**< Schema output options (see @ref schemaprinterflags). */
 };
 
 #define LEVEL ctx->level             /**< current level */
@@ -63,7 +64,7 @@
  * the @p text is printed completely as a NULL-terminated string.
  */
 static void
-ypr_encode(struct lyout *out, const char *text, int len)
+ypr_encode(struct lyp_out *out, const char *text, int len)
 {
     int i, start_len;
     const char *start;
@@ -93,19 +94,19 @@
         }
 
         if (special) {
-            ly_write(out, start, start_len);
+            lyp_write(out, start, start_len);
             switch (special) {
             case '\n':
-                ly_write(out, "\\n", 2);
+                lyp_write(out, "\\n", 2);
                 break;
             case '\t':
-                ly_write(out, "\\t", 2);
+                lyp_write(out, "\\t", 2);
                 break;
             case '\"':
-                ly_write(out, "\\\"", 2);
+                lyp_write(out, "\\\"", 2);
                 break;
             case '\\':
-                ly_write(out, "\\\\", 2);
+                lyp_write(out, "\\\\", 2);
                 break;
             }
 
@@ -116,15 +117,15 @@
         }
     }
 
-    ly_write(out, start, start_len);
+    lyp_write(out, start, start_len);
 }
 
 static void
-ypr_open(struct lyout *out, int *flag)
+ypr_open(struct lyp_out *out, int *flag)
 {
     if (flag && !*flag) {
         *flag = 1;
-        ly_print(out, " {\n");
+        lyp_print(out, " {\n");
     }
 }
 
@@ -132,9 +133,9 @@
 ypr_close(struct ypr_ctx *ctx, int flag)
 {
     if (flag) {
-        ly_print(ctx->out, "%*s}\n", INDENT);
+        lyp_print(ctx->out, "%*s}\n", INDENT);
     } else {
-        ly_print(ctx->out, ";\n");
+        lyp_print(ctx->out, ";\n");
     }
 }
 
@@ -144,28 +145,28 @@
     const char *s, *t;
 
     if (singleline) {
-        ly_print(ctx->out, "%*s%s \"", INDENT, name);
+        lyp_print(ctx->out, "%*s%s \"", INDENT, name);
     } else {
-        ly_print(ctx->out, "%*s%s\n", INDENT, name);
+        lyp_print(ctx->out, "%*s%s\n", INDENT, name);
         LEVEL++;
 
-        ly_print(ctx->out, "%*s\"", INDENT);
+        lyp_print(ctx->out, "%*s\"", INDENT);
     }
     t = text;
     while ((s = strchr(t, '\n'))) {
         ypr_encode(ctx->out, t, s - t);
-        ly_print(ctx->out, "\n");
+        lyp_print(ctx->out, "\n");
         t = s + 1;
         if (*t != '\n') {
-            ly_print(ctx->out, "%*s ", INDENT);
+            lyp_print(ctx->out, "%*s ", INDENT);
         }
     }
 
     ypr_encode(ctx->out, t, strlen(t));
     if (closed) {
-        ly_print(ctx->out, "\";\n");
+        lyp_print(ctx->out, "\";\n");
     } else {
-        ly_print(ctx->out, "\"");
+        lyp_print(ctx->out, "\"");
     }
     if (!singleline) {
         LEVEL--;
@@ -180,26 +181,26 @@
 
     if (stmt->arg) {
         if (stmt->flags) {
-            ly_print(ctx->out, "%*s%s\n", INDENT, stmt->stmt);
+            lyp_print(ctx->out, "%*s%s\n", INDENT, stmt->stmt);
             LEVEL++;
-            ly_print(ctx->out, "%*s%c", INDENT, (stmt->flags & LYS_DOUBLEQUOTED) ? '\"' : '\'');
+            lyp_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");
+                lyp_print(ctx->out, "\n");
                 t = s + 1;
                 if (*t != '\n') {
-                    ly_print(ctx->out, "%*s ", INDENT);
+                    lyp_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");
+            lyp_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");
+            lyp_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");
+        lyp_print(ctx->out, "%*s%s%s", INDENT, stmt->stmt, stmt->child ? " {\n" : ";\n");
     }
 
     if (stmt->child) {
@@ -208,7 +209,7 @@
             yprp_stmt(ctx, childstmt);
         }
         LEVEL--;
-        ly_print(ctx->out, "%*s}\n", INDENT);
+        lyp_print(ctx->out, "%*s}\n", INDENT);
     }
 }
 
@@ -238,7 +239,7 @@
         }
 
         if (!ext->compiled && ext->yin) {
-            ly_print(ctx->out, "%*s%s; // Model comes from different input format, extensions must be resolved first.\n", INDENT, ext[u].name);
+            lyp_print(ctx->out, "%*s%s; // Model comes from different input format, extensions must be resolved first.\n", INDENT, ext[u].name);
             continue;
         }
 
@@ -251,11 +252,11 @@
             argument = ext[u].argument;
         }
         if (argument) {
-            ly_print(ctx->out, "%*s%s \"", INDENT, ext[u].name);
+            lyp_print(ctx->out, "%*s%s \"", INDENT, ext[u].name);
             ypr_encode(ctx->out, argument, -1);
-            ly_print(ctx->out, "\"");
+            lyp_print(ctx->out, "\"");
         } else {
-            ly_print(ctx->out, "%*s%s", INDENT, ext[u].name);
+            lyp_print(ctx->out, "%*s%s", INDENT, ext[u].name);
         }
 
         child_presence = 0;
@@ -265,16 +266,16 @@
                 continue;
             }
             if (!child_presence) {
-                ly_print(ctx->out, " {\n");
+                lyp_print(ctx->out, " {\n");
                 child_presence = 1;
             }
             yprp_stmt(ctx, stmt);
         }
         LEVEL--;
         if (child_presence) {
-            ly_print(ctx->out, "%*s}\n", INDENT);
+            lyp_print(ctx->out, "%*s}\n", INDENT);
         } else {
-            ly_print(ctx->out, ";\n");
+            lyp_print(ctx->out, ";\n");
         }
     }
 }
@@ -317,7 +318,7 @@
     }
 
     if (ext_substmt_info[substmt].flags & SUBST_FLAG_ID) {
-        ly_print(ctx->out, "%*s%s %s", INDENT, ext_substmt_info[substmt].name, text);
+        lyp_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);
@@ -372,15 +373,15 @@
 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);
+        lyp_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);
+        lyp_print(ctx->out, "%*s}\n", INDENT);
     } else {
-        ly_print(ctx->out, "%*srevision %s;\n", INDENT, rev->date);
+        lyp_print(ctx->out, "%*srevision %s;\n", INDENT, rev->date);
     }
 }
 
@@ -449,7 +450,7 @@
         ypr_open(ctx->out, flag);
         extflag = 0;
 
-        ly_print(ctx->out, "%*sif-feature \"%s\"", INDENT, iff[u]);
+        lyp_print(ctx->out, "%*sif-feature \"%s\"", INDENT, iff[u]);
 
         /* extensions */
         LEVEL++;
@@ -476,14 +477,14 @@
     switch (op) {
     case LYS_IFF_F:
         if (ctx->module == feat->features[*index_f]->module) {
-            ly_print(ctx->out, "%s", feat->features[*index_f]->name);
+            lyp_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);
+            lyp_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 ");
+        lyp_print(ctx->out, "not ");
         yprc_iffeature(ctx, feat, index_e, index_f);
         break;
     case LYS_IFF_AND:
@@ -496,13 +497,13 @@
         /* falls through */
     case LYS_IFF_OR:
         if (brackets_flag) {
-            ly_print(ctx->out, "(");
+            lyp_print(ctx->out, "(");
         }
         yprc_iffeature(ctx, feat, index_e, index_f);
-        ly_print(ctx->out, " %s ", op == LYS_IFF_OR ? "or" : "and");
+        lyp_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, ")");
+            lyp_print(ctx->out, ")");
         }
     }
 }
@@ -519,9 +520,9 @@
         ypr_open(ctx->out, flag);
         extflag = 0;
 
-        ly_print(ctx->out, "%*sif-feature \"", INDENT);
+        lyp_print(ctx->out, "%*sif-feature \"", INDENT);
         yprc_iffeature(ctx, iff, &index_e, &index_f);
-        ly_print(ctx->out, "\"");
+        lyp_print(ctx->out, "\"");
 
         /* extensions */
         LEVEL++;
@@ -542,7 +543,7 @@
     int flag = 0, flag2 = 0;
     LY_ARRAY_SIZE_TYPE u;
 
-    ly_print(ctx->out, "%*sextension %s", INDENT, ext->name);
+    lyp_print(ctx->out, "%*sextension %s", INDENT, ext->name);
     LEVEL++;
 
     if (ext->exts) {
@@ -551,7 +552,7 @@
 
     if (ext->argument) {
         ypr_open(ctx->out, &flag);
-        ly_print(ctx->out, "%*sargument %s", INDENT, ext->argument);
+        lyp_print(ctx->out, "%*sargument %s", INDENT, ext->argument);
         LEVEL++;
         if (ext->exts) {
             u = -1;
@@ -581,7 +582,7 @@
 {
     int flag = 0;
 
-    ly_print(ctx->out, "\n%*sfeature %s", INDENT, feat->name);
+    lyp_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);
@@ -597,7 +598,7 @@
 {
     int flag = 0;
 
-    ly_print(ctx->out, "\n%*sfeature %s", INDENT, feat->name);
+    lyp_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);
@@ -614,7 +615,7 @@
     int flag = 0;
     LY_ARRAY_SIZE_TYPE u;
 
-    ly_print(ctx->out, "\n%*sidentity %s", INDENT, ident->name);
+    lyp_print(ctx->out, "\n%*sidentity %s", INDENT, ident->name);
     LEVEL++;
 
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, ident->exts, &flag, 0);
@@ -639,7 +640,7 @@
     int flag = 0;
     LY_ARRAY_SIZE_TYPE u;
 
-    ly_print(ctx->out, "\n%*sidentity %s", INDENT, ident->name);
+    lyp_print(ctx->out, "\n%*sidentity %s", INDENT, ident->name);
     LEVEL++;
 
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, ident->exts, &flag, 0);
@@ -648,9 +649,9 @@
     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);
+            lyp_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);
+            lyp_print(ctx->out, "%*sderived %s;\n", INDENT, ident->derived[u]->name);
         }
     }
 
@@ -672,9 +673,9 @@
     }
 
     ypr_open(ctx->out, flag);
-    ly_print(ctx->out, "%*s%s \"", INDENT, name);
+    lyp_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, "\"");
+    lyp_print(ctx->out, "\"");
 
     LEVEL++;
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, restr->exts, &inner_flag, 0);
@@ -704,9 +705,9 @@
     int inner_flag = 0;
 
     ypr_open(ctx->out, flag);
-    ly_print(ctx->out, "%*smust \"", INDENT);
+    lyp_print(ctx->out, "%*smust \"", INDENT);
     ypr_encode(ctx->out, must->cond->expr, -1);
-    ly_print(ctx->out, "\"");
+    lyp_print(ctx->out, "\"");
 
     LEVEL++;
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, must->exts, &inner_flag, 0);
@@ -736,26 +737,26 @@
     }
 
     ypr_open(ctx->out, flag);
-    ly_print(ctx->out, "%*s%s \"", INDENT, (basetype == LY_TYPE_STRING || basetype == LY_TYPE_BINARY) ? "length" : "range");
+    lyp_print(ctx->out, "%*s%s \"", INDENT, (basetype == LY_TYPE_STRING || basetype == LY_TYPE_BINARY) ? "length" : "range");
     LY_ARRAY_FOR(range->parts, u) {
         if (u > 0) {
-            ly_print(ctx->out, " | ");
+            lyp_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);
+                lyp_print(ctx->out, "%"PRIu64, range->parts[u].max_u64);
             } else { /* signed values */
-                ly_print(ctx->out, "%"PRId64, range->parts[u].max_64);
+                lyp_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);
+                lyp_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);
+                lyp_print(ctx->out, "%"PRId64"..%"PRId64, range->parts[u].min_64, range->parts[u].max_64);
             }
         }
     }
-    ly_print(ctx->out, "\"");
+    lyp_print(ctx->out, "\"");
 
     LEVEL++;
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, range->exts, &inner_flag, 0);
@@ -780,9 +781,9 @@
     int inner_flag = 0;
 
     ypr_open(ctx->out, flag);
-    ly_print(ctx->out, "%*spattern \"", INDENT);
+    lyp_print(ctx->out, "%*spattern \"", INDENT);
     ypr_encode(ctx->out, pattern->expr, -1);
-    ly_print(ctx->out, "\"");
+    lyp_print(ctx->out, "\"");
 
     LEVEL++;
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, pattern->exts, &inner_flag, 0);
@@ -816,9 +817,9 @@
     }
     ypr_open(ctx->out, flag);
 
-    ly_print(ctx->out, "%*swhen \"", INDENT);
+    lyp_print(ctx->out, "%*swhen \"", INDENT);
     ypr_encode(ctx->out, when->cond, -1);
-    ly_print(ctx->out, "\"");
+    lyp_print(ctx->out, "\"");
 
     LEVEL++;
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, when->exts, &inner_flag, 0);
@@ -838,9 +839,9 @@
     }
     ypr_open(ctx->out, flag);
 
-    ly_print(ctx->out, "%*swhen \"", INDENT);
+    lyp_print(ctx->out, "%*swhen \"", INDENT);
     ypr_encode(ctx->out, when->cond->expr, -1);
-    ly_print(ctx->out, "\"");
+    lyp_print(ctx->out, "\"");
 
     LEVEL++;
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, when->exts, &inner_flag, 0);
@@ -859,11 +860,11 @@
     LY_ARRAY_FOR(items, u) {
         ypr_open(ctx->out, flag);
         if (type == LY_TYPE_BITS) {
-            ly_print(ctx->out, "%*sbit %s", INDENT, items[u].name);
+            lyp_print(ctx->out, "%*sbit %s", INDENT, items[u].name);
         } else { /* LY_TYPE_ENUM */
-            ly_print(ctx->out, "%*senum \"", INDENT);
+            lyp_print(ctx->out, "%*senum \"", INDENT);
             ypr_encode(ctx->out, items[u].name, -1);
-            ly_print(ctx->out, "\"");
+            lyp_print(ctx->out, "\"");
         }
         inner_flag = 0;
         LEVEL++;
@@ -890,7 +891,7 @@
     LY_ARRAY_SIZE_TYPE u;
     int flag = 0;
 
-    ly_print(ctx->out, "%*stype %s", INDENT, type->name);
+    lyp_print(ctx->out, "%*stype %s", INDENT, type->name);
     LEVEL++;
 
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, type->exts, &flag, 0);
@@ -946,7 +947,7 @@
     LY_ARRAY_SIZE_TYPE u;
     int flag = 0;
 
-    ly_print(ctx->out, "%*stype %s", INDENT, lys_datatype2str(type->basetype));
+    lyp_print(ctx->out, "%*stype %s", INDENT, lys_datatype2str(type->basetype));
     LEVEL++;
 
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, type->exts, &flag, 0);
@@ -990,9 +991,9 @@
             int inner_flag = 0;
 
             ypr_open(ctx->out, &flag);
-            ly_print(ctx->out, "%*s%s \"", INDENT, type->basetype == LY_TYPE_BITS ? "bit" : "enum");
+            lyp_print(ctx->out, "%*s%s \"", INDENT, type->basetype == LY_TYPE_BITS ? "bit" : "enum");
             ypr_encode(ctx->out, item->name, -1);
-            ly_print(ctx->out, "\"");
+            lyp_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);
@@ -1064,7 +1065,7 @@
 {
     LYOUT_CHECK(ctx->out);
 
-    ly_print(ctx->out, "\n%*stypedef %s {\n", INDENT, tpdf->name);
+    lyp_print(ctx->out, "\n%*stypedef %s {\n", INDENT, tpdf->name);
     LEVEL++;
 
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, tpdf->exts, NULL, 0);
@@ -1083,7 +1084,7 @@
     ypr_reference(ctx, tpdf->ref, tpdf->exts, NULL);
 
     LEVEL--;
-    ly_print(ctx->out, "%*s}\n", INDENT);
+    lyp_print(ctx->out, "%*s}\n", INDENT);
 }
 
 static void yprp_node(struct ypr_ctx *ctx, const struct lysp_node *node);
@@ -1099,7 +1100,7 @@
 
     LYOUT_CHECK(ctx->out);
 
-    ly_print(ctx->out, "\n%*sgrouping %s", INDENT, grp->name);
+    lyp_print(ctx->out, "\n%*sgrouping %s", INDENT, grp->name);
     LEVEL++;
 
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, grp->exts, &flag, 0);
@@ -1142,7 +1143,7 @@
     }
     ypr_open(ctx->out, flag);
 
-    ly_print(ctx->out, "\n%*s%s {\n", INDENT, (inout->nodetype == LYS_INPUT ? "input" : "output"));
+    lyp_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);
@@ -1176,7 +1177,7 @@
     }
     ypr_open(ctx->out, flag);
 
-    ly_print(ctx->out, "\n%*s%s {\n", INDENT, (&action->input == inout) ? "input" : "output");
+    lyp_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);
@@ -1184,8 +1185,10 @@
         yprc_must(ctx, &inout->musts[u], NULL);
     }
 
-    LY_LIST_FOR(inout->data, data) {
-        yprc_node(ctx, data);
+    if (!(ctx->options & LYS_OUTPUT_NO_SUBSTMT)) {
+        LY_LIST_FOR(inout->data, data) {
+            yprc_node(ctx, data);
+        }
     }
 
     LEVEL--;
@@ -1201,7 +1204,7 @@
 
     LYOUT_CHECK(ctx->out);
 
-    ly_print(ctx->out, "%*snotification %s", INDENT, notif->name);
+    lyp_print(ctx->out, "%*snotification %s", INDENT, notif->name);
 
     LEVEL++;
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, notif->exts, &flag, 0);
@@ -1242,7 +1245,7 @@
 
     LYOUT_CHECK(ctx->out);
 
-    ly_print(ctx->out, "%*snotification %s", INDENT, notif->name);
+    lyp_print(ctx->out, "%*snotification %s", INDENT, notif->name);
 
     LEVEL++;
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, notif->exts, &flag, 0);
@@ -1255,9 +1258,11 @@
     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);
+    if (!(ctx->options & LYS_OUTPUT_NO_SUBSTMT)) {
+        LY_LIST_FOR(notif->data, data) {
+            ypr_open(ctx->out, &flag);
+            yprc_node(ctx, data);
+        }
     }
 
     LEVEL--;
@@ -1272,7 +1277,7 @@
 
     LYOUT_CHECK(ctx->out);
 
-    ly_print(ctx->out, "%*s%s %s", INDENT, action->parent ? "action" : "rpc", action->name);
+    lyp_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);
@@ -1305,7 +1310,7 @@
 
     LYOUT_CHECK(ctx->out);
 
-    ly_print(ctx->out, "%*s%s %s", INDENT, action->parent ? "action" : "rpc", action->name);
+    lyp_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);
@@ -1324,7 +1329,7 @@
 static void
 yprp_node_common1(struct ypr_ctx *ctx, const struct lysp_node *node, int *flag)
 {
-    ly_print(ctx->out, "%*s%s %s%s", INDENT, lys_nodetype2str(node->nodetype), node->name, flag ? "" : " {\n");
+    lyp_print(ctx->out, "%*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);
@@ -1337,7 +1342,7 @@
 {
     LY_ARRAY_SIZE_TYPE u;
 
-    ly_print(ctx->out, "%*s%s %s%s", INDENT, lys_nodetype2str(node->nodetype), node->name, flag ? "" : " {\n");
+    lyp_print(ctx->out, "%*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);
@@ -1440,19 +1445,21 @@
 
     yprc_node_common2(ctx, node, &flag);
 
-    LY_LIST_FOR(cont->child, child) {
-        ypr_open(ctx->out, &flag);
-        yprc_node(ctx, child);
-    }
+    if (!(ctx->options & LYS_OUTPUT_NO_SUBSTMT)) {
+        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->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]);
+        LY_ARRAY_FOR(cont->notifs, u) {
+            ypr_open(ctx->out, &flag);
+            yprc_notification(ctx, &cont->notifs[u]);
+        }
     }
 
     LEVEL--;
@@ -1487,9 +1494,11 @@
     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);
+    if (!(ctx->options & LYS_OUTPUT_NO_SUBSTMT)) {
+        for (child = cs->child; child && child->parent == (struct lysc_node*)cs; child = child->next) {
+            ypr_open(ctx->out, &flag);
+            yprc_node(ctx, child);
+        }
     }
 
     LEVEL--;
@@ -1564,7 +1573,7 @@
     yprp_node_common2(ctx, node, NULL);
 
     LEVEL--;
-    ly_print(ctx->out, "%*s}\n", INDENT);
+    lyp_print(ctx->out, "%*s}\n", INDENT);
 }
 
 static void
@@ -1588,7 +1597,7 @@
     yprc_node_common2(ctx, node, NULL);
 
     LEVEL--;
-    ly_print(ctx->out, "%*s}\n", INDENT);
+    lyp_print(ctx->out, "%*s}\n", INDENT);
 }
 
 static void
@@ -1630,7 +1639,7 @@
     ypr_reference(ctx, node->ref, node->exts, NULL);
 
     LEVEL--;
-    ly_print(ctx->out, "%*s}\n", INDENT);
+    lyp_print(ctx->out, "%*s}\n", INDENT);
 }
 
 static void
@@ -1666,7 +1675,7 @@
     ypr_reference(ctx, node->ref, node->exts, NULL);
 
     LEVEL--;
-    ly_print(ctx->out, "%*s}\n", INDENT);
+    lyp_print(ctx->out, "%*s}\n", INDENT);
 }
 
 static void
@@ -1756,17 +1765,17 @@
     }
     if (!(list->flags & LYS_KEYLESS)) {
         ypr_open(ctx->out, &flag);
-        ly_print(ctx->out, "%*skey \"", INDENT);
+        lyp_print(ctx->out, "%*skey \"", INDENT);
         for (struct lysc_node *key = list->child; key && key->nodetype == LYS_LEAF && (key->flags & LYS_KEY); key = key->next) {
-            ly_print(ctx->out, "%s%s", u > 0 ? ", " : "", key->name);
+            lyp_print(ctx->out, "%s%s", u > 0 ? ", " : "", key->name);
         }
-        ly_print(ctx->out, "\";\n");
+        lyp_print(ctx->out, "\";\n");
     }
     LY_ARRAY_FOR(list->uniques, u) {
         ypr_open(ctx->out, &flag);
-        ly_print(ctx->out, "%*sunique \"", INDENT);
+        lyp_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);
+            lyp_print(ctx->out, "%s%s", v > 0 ? ", " : "", list->uniques[u][v]->name);
         }
         ypr_close(ctx, 0);
     }
@@ -1786,19 +1795,21 @@
     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);
-    }
+    if (!(ctx->options & LYS_OUTPUT_NO_SUBSTMT)) {
+        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->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]);
+        LY_ARRAY_FOR(list->notifs, u) {
+            ypr_open(ctx->out, &flag);
+            yprc_notification(ctx, &list->notifs[u]);
+        }
     }
 
     LEVEL--;
@@ -1811,7 +1822,7 @@
     LY_ARRAY_SIZE_TYPE u;
     int flag = 0;
 
-    ly_print(ctx->out, "%*srefine \"%s\"", INDENT, refine->nodeid);
+    lyp_print(ctx->out, "%*srefine \"%s\"", INDENT, refine->nodeid);
     LEVEL++;
 
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, refine->exts, &flag, 0);
@@ -1861,7 +1872,7 @@
     LY_ARRAY_SIZE_TYPE u;
     struct lysp_node *child;
 
-    ly_print(ctx->out, "%*saugment \"%s\" {\n", INDENT, aug->nodeid);
+    lyp_print(ctx->out, "%*saugment \"%s\" {\n", INDENT, aug->nodeid);
     LEVEL++;
 
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, aug->exts, NULL, 0);
@@ -2027,7 +2038,7 @@
     struct lysp_deviate_del *del;
     struct lysp_deviate *elem;
 
-    ly_print(ctx->out, "%*sdeviation \"%s\" {\n", INDENT, deviation->nodeid);
+    lyp_print(ctx->out, "%*sdeviation \"%s\" {\n", INDENT, deviation->nodeid);
     LEVEL++;
 
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, deviation->exts, NULL, 0);
@@ -2035,20 +2046,20 @@
     ypr_reference(ctx, deviation->ref, deviation->exts, NULL);
 
     LY_LIST_FOR(deviation->deviates, elem) {
-        ly_print(ctx->out, "%*sdeviate ", INDENT);
+        lyp_print(ctx->out, "%*sdeviate ", INDENT);
         if (elem->mod == LYS_DEV_NOT_SUPPORTED) {
             if (elem->exts) {
-                ly_print(ctx->out, "not-supported {\n");
+                lyp_print(ctx->out, "not-supported {\n");
                 LEVEL++;
 
                 yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, elem->exts, NULL, 0);
             } else {
-                ly_print(ctx->out, "not-supported;\n");
+                lyp_print(ctx->out, "not-supported;\n");
                 continue;
             }
         } else if (elem->mod == LYS_DEV_ADD) {
             add = (struct lysp_deviate_add*)elem;
-            ly_print(ctx->out, "add {\n");
+            lyp_print(ctx->out, "add {\n");
             LEVEL++;
 
             yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, add->exts, NULL, 0);
@@ -2076,7 +2087,7 @@
             }
         } else if (elem->mod == LYS_DEV_REPLACE) {
             rpl = (struct lysp_deviate_rpl*)elem;
-            ly_print(ctx->out, "replace {\n");
+            lyp_print(ctx->out, "replace {\n");
             LEVEL++;
 
             yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, rpl->exts, NULL, 0);
@@ -2099,7 +2110,7 @@
             }
         } else if (elem->mod == LYS_DEV_DELETE) {
             del = (struct lysp_deviate_del*)elem;
-            ly_print(ctx->out, "delete {\n");
+            lyp_print(ctx->out, "delete {\n");
             LEVEL++;
 
             yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, del->exts, NULL, 0);
@@ -2144,7 +2155,7 @@
 
     /* meta-stmts */
     if (module->org || module->contact || module->dsc || module->ref) {
-        ly_print(ctx->out, "\n");
+        lyp_print(ctx->out, "\n");
     }
     ypr_substmt(ctx, LYEXT_SUBSTMT_ORGANIZATION, 0, module->org, NULL);
     ypr_substmt(ctx, LYEXT_SUBSTMT_CONTACT, 0, module->contact, NULL);
@@ -2153,29 +2164,29 @@
 
     /* revision-stmts */
     if (module->revision) {
-        ly_print(ctx->out, "\n%*srevision %s;\n", INDENT, module->revision);
+        lyp_print(ctx->out, "\n%*srevision %s;\n", INDENT, module->revision);
     }
 
     LEVEL--;
-    ly_print(ctx->out, "%*s}\n", INDENT);
+    lyp_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)
+yang_print_parsed(struct lyp_out *out, const struct lys_module *module)
 {
     LY_ARRAY_SIZE_TYPE 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);
+    lyp_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);
+        lyp_print(ctx->out, "%*s/* PARSED INFORMATION ARE NOT FULLY PRESENT */\n", INDENT);
         return ypr_missing_format(ctx, module);
     }
 
@@ -2190,7 +2201,7 @@
 
     /* linkage-stmts */
     LY_ARRAY_FOR(modp->imports, u) {
-        ly_print(out, "%s%*simport %s {\n", u ? "" : "\n", INDENT, modp->imports[u].module->name);
+        lyp_print(out, "%s%*simport %s {\n", u ? "" : "\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);
@@ -2200,11 +2211,11 @@
         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);
+        lyp_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, "%s%*sinclude %s {\n", u ? "" : "\n",  INDENT, modp->includes[u].submodule->name);
+            lyp_print(out, "%s%*sinclude %s {\n", u ? "" : "\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]) {
@@ -2213,15 +2224,15 @@
             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);
+            lyp_print(out, "%*s}\n", INDENT);
         } else {
-            ly_print(out, "\n%*sinclude \"%s\";\n", INDENT, modp->includes[u].submodule->name);
+            lyp_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");
+        lyp_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);
@@ -2230,18 +2241,18 @@
 
     /* revision-stmts */
     if (modp->revs) {
-        ly_print(out, "\n");
+        lyp_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");
+        lyp_print(out, "\n");
         yprp_extension(ctx, &modp->extensions[u]);
     }
     if (modp->exts) {
-        ly_print(out, "\n");
+        lyp_print(out, "\n");
         yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, module->parsed->exts, NULL, 0);
     }
 
@@ -2282,25 +2293,36 @@
     }
 
     LEVEL--;
-    ly_print(out, "%*s}\n", INDENT);
+    lyp_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)
+yang_print_compiled_node(struct lyp_out *out, const struct lysc_node *node, int options)
+{
+    struct ypr_ctx ctx_ = {.out = out, .level = 0, .module = node->module, .options = options}, *ctx = &ctx_;
+
+    yprc_node(ctx, node);
+
+    ly_print_flush(out);
+    return LY_SUCCESS;
+}
+
+LY_ERR
+yang_print_compiled(struct lyp_out *out, const struct lys_module *module, int options)
 {
     LY_ARRAY_SIZE_TYPE u;
     struct lysc_node *data;
     struct lysc_module *modc = module->compiled;
-    struct ypr_ctx ctx_ = {.out = out, .level = 0, .module = module}, *ctx = &ctx_;
+    struct ypr_ctx ctx_ = {.out = out, .level = 0, .module = module, .options = options}, *ctx = &ctx_;
 
-    ly_print(ctx->out, "%*smodule %s {\n", INDENT, module->name);
+    lyp_print(ctx->out, "%*smodule %s {\n", INDENT, module->name);
     LEVEL++;
 
     if (!modc) {
-        ly_print(ctx->out, "%*s/* COMPILED INFORMATION ARE NOT PRESENT */\n", INDENT);
+        lyp_print(ctx->out, "%*s/* COMPILED INFORMATION ARE NOT PRESENT */\n", INDENT);
         return ypr_missing_format(ctx, module);
     }
 
@@ -2315,7 +2337,7 @@
 
     /* linkage-stmts */
     LY_ARRAY_FOR(modc->imports, u) {
-        ly_print(out, "\n%*simport %s {\n", INDENT, modc->imports[u].module->name);
+        lyp_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);
@@ -2323,12 +2345,12 @@
             ypr_substmt(ctx, LYEXT_SUBSTMT_REVISIONDATE, 0, modc->imports[u].module->revision, modc->imports[u].exts);
         }
         LEVEL--;
-        ly_print(out, "%*s}\n", INDENT);
+        lyp_print(out, "%*s}\n", INDENT);
     }
 
     /* meta-stmts */
     if (module->org || module->contact || module->dsc || module->ref) {
-        ly_print(out, "\n");
+        lyp_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);
@@ -2337,12 +2359,12 @@
 
     /* revision-stmts */
     if (module->revision) {
-        ly_print(ctx->out, "\n%*srevision %s;\n", INDENT, module->revision);
+        lyp_print(ctx->out, "\n%*srevision %s;\n", INDENT, module->revision);
     }
 
     /* body-stmts */
     if (modc->exts) {
-        ly_print(out, "\n");
+        lyp_print(out, "\n");
         yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, module->compiled->exts, NULL, 0);
     }
 
@@ -2354,20 +2376,22 @@
         yprc_identity(ctx, &modc->identities[u]);
     }
 
-    LY_LIST_FOR(modc->data, data) {
-        yprc_node(ctx, data);
-    }
+    if (!(ctx->options & LYS_OUTPUT_NO_SUBSTMT)) {
+        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->rpcs, u) {
+            yprc_action(ctx, &modc->rpcs[u]);
+        }
 
-    LY_ARRAY_FOR(modc->notifs, u) {
-        yprc_notification(ctx, &modc->notifs[u]);
+        LY_ARRAY_FOR(modc->notifs, u) {
+            yprc_notification(ctx, &modc->notifs[u]);
+        }
     }
 
     LEVEL--;
-    ly_print(out, "%*s}\n", INDENT);
+    lyp_print(out, "%*s}\n", INDENT);
     ly_print_flush(out);
 
     return LY_SUCCESS;
diff --git a/src/printer_yin.c b/src/printer_yin.c
index 95dddd3..c27404a 100644
--- a/src/printer_yin.c
+++ b/src/printer_yin.c
@@ -34,7 +34,7 @@
  * @brief YIN printer context.
  */
 struct ypr_ctx {
-    struct lyout *out;               /**< output specification */
+    struct lyp_out *out;               /**< output specification */
     unsigned int level;              /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
     const struct lys_module *module; /**< schema to print */
 };
@@ -48,14 +48,14 @@
 static void
 ypr_open(struct ypr_ctx *ctx, const char *elem_name, const char *attr_name, const char *attr_value,  int flag)
 {
-    ly_print(ctx->out, "%*s<%s", INDENT, elem_name);
+    lyp_print(ctx->out, "%*s<%s", INDENT, elem_name);
 
     if (attr_name) {
-        ly_print(ctx->out, " %s=\"", attr_name);
+        lyp_print(ctx->out, " %s=\"", attr_name);
         lyxml_dump_text(ctx->out, attr_value, 1);
-        ly_print(ctx->out, "\"%s", flag == -1 ? "/>\n" : flag == 1 ? ">\n" : "");
+        lyp_print(ctx->out, "\"%s", flag == -1 ? "/>\n" : flag == 1 ? ">\n" : "");
     } else if (flag) {
-        ly_print(ctx->out, flag == -1 ? "/>\n" : ">\n");
+        lyp_print(ctx->out, flag == -1 ? "/>\n" : ">\n");
     }
 }
 
@@ -63,9 +63,9 @@
 ypr_close(struct ypr_ctx *ctx, const char *elem_name, int flag)
 {
     if (flag) {
-        ly_print(ctx->out, "%*s</%s>\n", INDENT, elem_name);
+        lyp_print(ctx->out, "%*s</%s>\n", INDENT, elem_name);
     } else {
-        ly_print(ctx->out, "/>\n");
+        lyp_print(ctx->out, "/>\n");
     }
 }
 
@@ -79,16 +79,16 @@
 {
     if (par_close_flag && !(*par_close_flag)) {
         (*par_close_flag) = 1;
-        ly_print(ctx->out, ">\n");
+        lyp_print(ctx->out, ">\n");
     }
 }
 
 static void
 ypr_yin_arg(struct ypr_ctx *ctx, const char *arg, const char *text)
 {
-    ly_print(ctx->out, "%*s<%s>", INDENT, arg);
+    lyp_print(ctx->out, "%*s<%s>", INDENT, arg);
     lyxml_dump_text(ctx->out, text, 0);
-    ly_print(ctx->out, "</%s>\n", arg);
+    lyp_print(ctx->out, "</%s>\n", arg);
 }
 
 
@@ -236,7 +236,7 @@
         ypr_close_parent(ctx, flag);
         extflag = 0;
 
-        ly_print(ctx->out, "%*s<if-feature name=\"%s",  INDENT, iff[u]);
+        lyp_print(ctx->out, "%*s<if-feature name=\"%s",  INDENT, iff[u]);
 
         /* extensions */
         LEVEL++;
@@ -247,7 +247,7 @@
             yprp_extension_instances(ctx, LYEXT_SUBSTMT_IFFEATURE, u, &exts[u], &extflag, 1);
         }
         LEVEL--;
-        ly_print(ctx->out, "\"/>\n");
+        lyp_print(ctx->out, "\"/>\n");
     }
 }
 
@@ -345,9 +345,9 @@
         return;
     }
 
-    ly_print(ctx->out, "%*s<%s %s=\"", INDENT, name, attr);
+    lyp_print(ctx->out, "%*s<%s %s=\"", INDENT, name, attr);
     lyxml_dump_text(ctx->out, (restr->arg[0] != 0x15 && restr->arg[0] != 0x06) ? restr->arg : &restr->arg[1], 1);
-    ly_print(ctx->out, "\"");
+    lyp_print(ctx->out, "\"");
 
     LEVEL++;
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, restr->exts, &inner_flag, 0);
@@ -381,9 +381,9 @@
         return;
     }
 
-    ly_print(ctx->out, "%*s<when condition=\"", INDENT);
+    lyp_print(ctx->out, "%*s<when condition=\"", INDENT);
     lyxml_dump_text(ctx->out, when->cond, 1);
-    ly_print(ctx->out, "\"");
+    lyp_print(ctx->out, "\"");
 
     LEVEL++;
     yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, when->exts, &inner_flag, 0);
@@ -402,13 +402,13 @@
 
     LY_ARRAY_FOR(items, u) {
         if (type == LY_TYPE_BITS) {
-            ly_print(ctx->out, "%*s<bit name=\"", INDENT);
+            lyp_print(ctx->out, "%*s<bit name=\"", INDENT);
             lyxml_dump_text(ctx->out, items[u].name, 1);
-            ly_print(ctx->out, "\"");
+            lyp_print(ctx->out, "\"");
         } else { /* LY_TYPE_ENUM */
-            ly_print(ctx->out, "%*s<enum name=\"", INDENT);
+            lyp_print(ctx->out, "%*s<enum name=\"", INDENT);
             lyxml_dump_text(ctx->out, items[u].name, 1);
-            ly_print(ctx->out, "\"");
+            lyp_print(ctx->out, "\"");
         }
         inner_flag = 0;
         LEVEL++;
@@ -1099,20 +1099,20 @@
     ypr_reference(ctx, deviation->ref, deviation->exts, NULL);
 
     LY_LIST_FOR(deviation->deviates, elem) {
-        ly_print(ctx->out, "%*s<deviate value=\"", INDENT);
+        lyp_print(ctx->out, "%*s<deviate value=\"", INDENT);
         if (elem->mod == LYS_DEV_NOT_SUPPORTED) {
             if (elem->exts) {
-                ly_print(ctx->out, "not-supported\"/>\n");
+                lyp_print(ctx->out, "not-supported\"/>\n");
                 LEVEL++;
 
                 yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, elem->exts, NULL, 0);
             } else {
-                ly_print(ctx->out, "not-supported\"/>\n");
+                lyp_print(ctx->out, "not-supported\"/>\n");
                 continue;
             }
         } else if (elem->mod == LYS_DEV_ADD) {
             add = (struct lysp_deviate_add*)elem;
-            ly_print(ctx->out, "add\">\n");
+            lyp_print(ctx->out, "add\">\n");
             LEVEL++;
 
             yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, add->exts, NULL, 0);
@@ -1140,7 +1140,7 @@
             }
         } else if (elem->mod == LYS_DEV_REPLACE) {
             rpl = (struct lysp_deviate_rpl*)elem;
-            ly_print(ctx->out, "replace\">\n");
+            lyp_print(ctx->out, "replace\">\n");
             LEVEL++;
 
             yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, rpl->exts, NULL, 0);
@@ -1163,7 +1163,7 @@
             }
         } else if (elem->mod == LYS_DEV_DELETE) {
             del = (struct lysp_deviate_del*)elem;
-            ly_print(ctx->out, "delete\">\n");
+            lyp_print(ctx->out, "delete\">\n");
             LEVEL++;
 
             yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, del->exts, NULL, 0);
@@ -1206,7 +1206,7 @@
 
     /* meta-stmts */
     if (module->org || module->contact || module->dsc || module->ref) {
-        ly_print(ctx->out, "\n");
+        lyp_print(ctx->out, "\n");
     }
     ypr_substmt(ctx, LYEXT_SUBSTMT_ORGANIZATION, 0, module->org, NULL);
     ypr_substmt(ctx, LYEXT_SUBSTMT_CONTACT, 0, module->contact, NULL);
@@ -1230,13 +1230,13 @@
 {
     LY_ARRAY_SIZE_TYPE u;
 
-    ly_print(ctx->out, "%*sxmlns=\"%s\"", indent + INDENT, YIN_NS_URI);
-    ly_print(ctx->out, "\n%*sxmlns:%s=\"%s\"", indent + INDENT, module->prefix, module->ns);
+    lyp_print(ctx->out, "%*sxmlns=\"%s\"", indent + INDENT, YIN_NS_URI);
+    lyp_print(ctx->out, "\n%*sxmlns:%s=\"%s\"", indent + INDENT, module->prefix, module->ns);
 
     struct lysp_module *modp = module->parsed;
 
     LY_ARRAY_FOR(modp->imports, u){
-        ly_print(ctx->out, "\n%*sxmlns:%s=\"%s\"", indent + INDENT, modp->imports[u].prefix, modp->imports[u].module->ns);
+        lyp_print(ctx->out, "\n%*sxmlns:%s=\"%s\"", indent + INDENT, modp->imports[u].prefix, modp->imports[u].module->ns);
     }
 }
 
@@ -1375,7 +1375,7 @@
         }
 
         if (!ext->compiled && ext->yin) {
-            ly_print(ctx->out, "%*s<%s/> <!-- Model comes from different input format, extensions must be resolved first. -->\n", INDENT, ext[u].name);
+            lyp_print(ctx->out, "%*s<%s/> <!-- Model comes from different input format, extensions must be resolved first. -->\n", INDENT, ext[u].name);
             continue;
         }
 
@@ -1419,22 +1419,22 @@
 }
 
 LY_ERR
-yin_print_parsed(struct lyout *out, const struct lys_module *module)
+yin_print_parsed(struct lyp_out *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}, *ctx = &ctx_;
 
-    ly_print(ctx->out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-    ly_print(ctx->out, "%*s<module name=\"%s\"\n", INDENT, module->name);
+    lyp_print(ctx->out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+    lyp_print(ctx->out, "%*s<module name=\"%s\"\n", INDENT, module->name);
     ypr_xmlns(ctx, module, 8);
-    ly_print(ctx->out, ">\n");
+    lyp_print(ctx->out, ">\n");
 
     LEVEL++;
 
     if (!modp) {
-        ly_print(ctx->out, "%*s<!-- PARSED INFORMATION ARE NOT FULLY PRESENT -->\n", INDENT);
+        lyp_print(ctx->out, "%*s<!-- PARSED INFORMATION ARE NOT FULLY PRESENT -->\n", INDENT);
         return ypr_missing_format(ctx, module);
     }
 
@@ -1472,7 +1472,7 @@
             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);
+            lyp_print(out, "%*s}\n", INDENT);
         } else {
             ypr_open(ctx, "include", "module", modp->includes[u].submodule->name, -1);
         }
@@ -1480,7 +1480,7 @@
 
     /* meta-stmts */
     if (module->org || module->contact || module->dsc || module->ref) {
-        ly_print(out, "\n");
+        lyp_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);
@@ -1489,18 +1489,18 @@
 
     /* revision-stmts */
     if (modp->revs) {
-        ly_print(out, "\n");
+        lyp_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");
+        lyp_print(out, "\n");
         yprp_extension(ctx, &modp->extensions[u]);
     }
     if (modp->exts) {
-        ly_print(out, "\n");
+        lyp_print(out, "\n");
         yprp_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, module->parsed->exts, NULL, 0);
     }
 
@@ -1541,7 +1541,7 @@
     }
 
     LEVEL--;
-    ly_print(out, "%*s</module>\n", INDENT);
+    lyp_print(out, "%*s</module>\n", INDENT);
     ly_print_flush(out);
 
     return LY_SUCCESS;
diff --git a/src/tree.h b/src/tree.h
index d1313b7..e63cfc1 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -24,6 +24,14 @@
 #endif
 
 /**
+ * @defgroup trees Trees
+ *
+ * Generic macros, functions, etc. to work with both [schema](@ref schematree) and [data](@ref datatree) trees.
+ *
+ * @{
+ */
+
+/**
  * @brief Type (i.e. size) of the [sized array](@ref sizedarrays)'s size counter.
  *
  * To print the value via a print format, use LY_PRI_ARRAY_SIZE_TYPE specifier.
@@ -70,13 +78,6 @@
          ++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!
@@ -165,7 +166,7 @@
  */
 extern const char* ly_data_type2str[LY_DATA_TYPE_COUNT];
 
-/** @} */
+/** @} trees */
 
 #ifdef __cplusplus
 }
diff --git a/src/tree_data.h b/src/tree_data.h
index b3a2ab4..5d7d480 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -31,6 +31,7 @@
 
 /**
  * @defgroup datatree Data Tree
+ * @ingroup trees
  * @{
  *
  * Data structures and functions to manipulate and access instance data tree.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 9a2ee1f..f5a70b7 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -186,6 +186,73 @@
 }
 
 API const struct lysc_node *
+lys_find_node(struct ly_ctx *ctx, const struct lysc_node *context_node, const char *qpath)
+{
+    const char *id = qpath;
+    const char *prefix, *name;
+    size_t prefix_len, name_len;
+    unsigned int u;
+    const struct lysc_node *node = context_node;
+    struct lys_module *mod = NULL;
+
+    LY_CHECK_ARG_RET(ctx, qpath, NULL);
+
+    while(*id) {
+        if (id[0] == '/') {
+            ++id;
+        }
+        /* precess ".." in relative paths */
+        while (!strncmp("../", id, 3)) {
+            id += 3;
+            if (!node) {
+                LOGERR(ctx, LY_EINVAL, "Invalid qpath \"%s\" - too many \"..\" in the path.", qpath);
+                return NULL;
+            }
+            node = node->parent;
+        }
+
+        if (ly_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len) != LY_SUCCESS) {
+            LOGERR(ctx, LY_EINVAL, "Invalid qpath \"%s\" - invalid nodeid \"%.*s\".", qpath, id- qpath, qpath);
+            return NULL;
+        }
+        if (prefix) {
+            if (context_node) {
+                mod = lys_module_find_prefix(context_node->module, prefix, prefix_len);
+            } else {
+                for (u = 0; u < ctx->list.count; ++u) {
+                    if (!ly_strncmp(((struct lys_module *)ctx->list.objs[u])->name, prefix, prefix_len)) {
+                        struct lys_module *m = (struct lys_module *)ctx->list.objs[u];
+                        if (mod) {
+                            if (m->implemented) {
+                                mod = m;
+                                break;
+                            } else if (m->latest_revision) {
+                                mod = m;
+                            }
+                        } else {
+                            mod = m;
+                        }
+                    }
+                }
+            }
+        }
+        if (!mod) {
+            LOGERR(ctx, LY_EINVAL, "Invalid qpath - unable to find module connected with the prefix of the node \"%.*s\".",
+                   id - qpath, qpath);
+            return NULL;
+        }
+
+        node = lys_find_child(node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+        if (!node) {
+            LOGERR(ctx, LY_EINVAL, "Invalid qpath - unable to find \"%.*s\".", id - qpath, qpath);
+            return NULL;
+        }
+    }
+
+    return node;
+}
+
+API const struct lysc_node *
 lys_find_child(const struct lysc_node *parent, const struct lys_module *module, const char *name, size_t name_len,
                uint16_t nodetype, int options)
 {
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 94786b1..b3139dc 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -32,6 +32,14 @@
 #endif
 
 /**
+ * @defgroup schematree Schema Tree
+ * @ingroup trees
+ * @{
+ *
+ * Data structures and functions to manipulate and access schema tree.
+ */
+
+/**
  * @brief Macro to iterate via all elements in a schema tree which can be instantiated in data tree
  * (skips cases, input, output). This is the opening part to the #LYSC_TREE_DFS_END - they always have to be used together.
  *
@@ -134,8 +142,6 @@
     LYS_OUT_YIN = 3,     /**< YIN schema output format */
 
     LYS_OUT_TREE,        /**< Tree schema output format, for more information see the [printers](@ref howtoschemasprinters) page */
-    LYS_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 */
 } LYS_OUTFORMAT;
 
 #define LY_REV_SIZE 11   /**< revision data string length (including terminating NULL byte) */
@@ -579,7 +585,7 @@
 #define LYS_DEV_ADD 2                /**< deviate type add */
 #define LYS_DEV_DELETE 3             /**< deviate type delete */
 #define LYS_DEV_REPLACE 4            /**< deviate type replace */
-/** @} */
+/** @} deviatetypes */
 
 /**
  * @brief Generic deviate structure to get type and cast to lysp_deviate_* structure
@@ -742,7 +748,6 @@
 
 /**
  * @defgroup snodeflags Schema nodes flags
- * @ingroup schematree
  * @{
  */
 #define LYS_CONFIG_W     0x01        /**< config true; also set for input children nodes */
@@ -807,7 +812,7 @@
 #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 */
-/** @} */
+/** @} snodeflags */
 
 /**
  * @brief Generic YANG data node
@@ -1208,9 +1213,7 @@
 #define LYS_IFF_AND  0x01 /**< operand "and" */
 #define LYS_IFF_OR   0x02 /**< operand "or" */
 #define LYS_IFF_F    0x03 /**< feature */
-/**
- * @}
- */
+/** @} ifftokens */
 
 /**
  * @brief Compiled YANG revision statement
@@ -1908,8 +1911,6 @@
 
 /**
  * @defgroup sgetnextflags lys_getnext() flags
- * @ingroup schematree
- *
  * @{
  */
 #define LYS_GETNEXT_WITHCHOICE   0x01 /**< lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
@@ -1938,6 +1939,25 @@
                                        const char *name, size_t name_len, uint16_t nodetype, int options);
 
 /**
+ * @brief Get schema node specified by the schema path.
+ *
+ * In case the @p qpath uses prefixes (from imports or of the schema itself), the @p context_node must be specified
+ * even if the path is absolute. In case the @p context_node is not provided, the names of the schemas are expected as the
+ * node's prefixes in the @qpath. It also means that the relative paths are accepted only with the schema prefixes,
+ * not the full names.
+ *
+ * @param[in] ctx libyang context for logging and getting the correct schema if @p contet_node not provided.
+ * @param[in] context_node Context node for relative paths and/or as a source of the context module to resolve node's
+ * prefixes in @qpath.
+ * @param[in] qpath Schema path to be resolved. Not prefixed nodes inherits the prefix from its parent nodes. It means
+ * that the first node in the path must be prefixed. Both, import prefixes as well as full schema names are accepted as
+ * prefixes according to the @p context_node parameter presence.
+ * @return NULL in case of invalid path.
+ * @return found schema node.
+ */
+const struct lysc_node *lys_find_node(struct ly_ctx *ctx, const struct lysc_node *context_node, const char *qpath);
+
+/**
  * @brief Make the specific module implemented.
  *
  * @param[in] mod Module to make implemented. It is not an error
@@ -1985,7 +2005,7 @@
  */
 const char *lys_nodetype2str(uint16_t nodetype);
 
-/** @} */
+/** @} schematree */
 
 #ifdef __cplusplus
 }
diff --git a/src/xml.c b/src/xml.c
index f376672..b682960 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -1025,7 +1025,7 @@
 }
 
 LY_ERR
-lyxml_dump_text(struct lyout *out, const char *text, int attribute)
+lyxml_dump_text(struct lyp_out *out, const char *text, int attribute)
 {
     LY_ERR ret = LY_SUCCESS;
     unsigned int u;
@@ -1037,23 +1037,23 @@
     for (u = 0; text[u]; u++) {
         switch (text[u]) {
         case '&':
-            ret = ly_print(out, "&amp;");
+            ret = lyp_print(out, "&amp;");
             break;
         case '<':
-            ret = ly_print(out, "&lt;");
+            ret = lyp_print(out, "&lt;");
             break;
         case '>':
             /* not needed, just for readability */
-            ret = ly_print(out, "&gt;");
+            ret = lyp_print(out, "&gt;");
             break;
         case '"':
             if (attribute) {
-                ret = ly_print(out, "&quot;");
+                ret = lyp_print(out, "&quot;");
                 break;
             }
             /* falls through */
         default:
-            ly_write(out, &text[u], 1);
+            lyp_write(out, &text[u], 1);
         }
     }
 
diff --git a/src/xml.h b/src/xml.h
index 2bbf5da..c4ade27 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -22,7 +22,7 @@
 #include "set.h"
 #include "tree_schema.h"
 
-struct lyout;
+struct lyp_out;
 struct ly_prefix;
 
 /* Macro to test if character is whitespace */
@@ -128,7 +128,7 @@
  * @param[in] attribute Flag for attribute's value where a double quotes must be replaced.
  * @return LY_ERR values.
  */
-LY_ERR lyxml_dump_text(struct lyout *out, const char *text, int attribute);
+LY_ERR lyxml_dump_text(struct lyp_out *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 6f7937f..9f9dde4 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -400,6 +400,8 @@
             buf = strdup("");
             LY_CHECK_ERR_RET(!buf, LOGMEM(LYD_NODE_CTX(node)), LY_EMEM);
         } else {
+            struct lyp_out *out;
+
             switch (any->value_type) {
             case LYD_ANYDATA_STRING:
             case LYD_ANYDATA_XML:
@@ -408,8 +410,10 @@
                 LY_CHECK_ERR_RET(!buf, LOGMEM(LYD_NODE_CTX(node)), LY_EMEM);
                 break;
             case LYD_ANYDATA_DATATREE:
-                rc = lyd_print_mem(&buf, any->value.tree, LYD_XML, LYDP_WITHSIBLINGS);
-                LY_CHECK_RET(rc);
+                out = lyp_new_memory(&buf, 0);
+                rc = lyd_print(out, any->value.tree, LYD_XML, LYDP_WITHSIBLINGS);
+                lyp_free(out, NULL, 0);
+                LY_CHECK_RET(rc < 0, -rc);
                 break;
             /* TODO case LYD_ANYDATA_LYB:
                 LOGERR(LYD_NODE_CTX(node), LY_EINVAL, "Cannot convert LYB anydata into string.");
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index e0357d7..a40fdf5 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -175,6 +175,9 @@
     char *str;
     struct lyd_node *tree;
 
+    struct lyp_out *out;
+    assert_non_null(out = lyp_new_memory(&str, 0));
+
     data =
     "<any xmlns=\"urn:tests:a\">"
         "<element1>"
@@ -187,7 +190,7 @@
     assert_int_equal(LYS_ANYDATA, tree->schema->nodetype);
     assert_string_equal("any", tree->schema->name);
 
-    lyd_print_mem(&str, tree, LYD_XML, 0);
+    lyd_print(out, tree, LYD_XML, 0);
     assert_string_equal(str,
         "<any xmlns=\"urn:tests:a\">"
             "<element1>"
@@ -196,9 +199,11 @@
             "<element1a/>"
         "</any>"
     );
-    free(str);
+    lyp_out_reset(out);
 
     lyd_free_all(tree);
+    lyp_free(out, NULL, 1);
+
     *state = NULL;
 }
 
@@ -319,6 +324,9 @@
     char *str;
     struct lyd_node *tree;
 
+    struct lyp_out *out;
+    assert_non_null(out = lyp_new_memory(&str, 0));
+
     /* invalid value, no flags */
     data = "<foo3 xmlns=\"urn:tests:a\"/>";
     assert_int_equal(LY_EVALID, lyd_parse_xml_data(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
@@ -332,9 +340,9 @@
     assert_string_equal(((struct lyd_node_opaq *)tree)->name, "foo3");
     assert_string_equal(((struct lyd_node_opaq *)tree)->value, "");
 
-    lyd_print_mem(&str, tree, LYD_XML, 0);
+    lyd_print(out, tree, LYD_XML, 0);
     assert_string_equal(str, "<foo3 xmlns=\"urn:tests:a\"/>");
-    free(str);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     /* missing key, no flags */
@@ -350,9 +358,9 @@
     assert_string_equal(((struct lyd_node_opaq *)tree)->name, "l1");
     assert_string_equal(((struct lyd_node_opaq *)tree)->value, "");
 
-    lyd_print_mem(&str, tree, LYD_XML, 0);
+    lyd_print(out, tree, LYD_XML, 0);
     assert_string_equal(str, data);
-    free(str);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     /* invalid key, no flags */
@@ -368,9 +376,9 @@
     assert_string_equal(((struct lyd_node_opaq *)tree)->name, "l1");
     assert_string_equal(((struct lyd_node_opaq *)tree)->value, "");
 
-    lyd_print_mem(&str, tree, LYD_XML, 0);
+    lyd_print(out, tree, LYD_XML, 0);
     assert_string_equal(str, data);
-    free(str);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     /* opaq flag and fail */
@@ -378,6 +386,8 @@
             LYD_OPT_OPAQ | LYD_VALOPT_DATA_ONLY, &tree));
     assert_null(tree);
 
+    lyp_free(out, NULL, 1);
+
     *state = NULL;
 }
 
@@ -391,6 +401,9 @@
     struct lyd_node *tree, *op;
     const struct lyd_node *node;
 
+    struct lyp_out *out;
+    assert_non_null(out = lyp_new_memory(&str, 0));
+
     data =
         "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" msgid=\"25\" custom-attr=\"val\">"
             "<edit-config>"
@@ -434,7 +447,7 @@
     assert_null(node->schema);
     assert_string_equal(((struct lyd_node_opaq *)node)->name, "z");
 
-    lyd_print_mem(&str, tree, LYD_XML, 0);
+    lyd_print(out, tree, LYD_XML, 0);
     assert_string_equal(str,
         "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" msgid=\"25\" custom-attr=\"val\">"
             "<edit-config>"
@@ -453,12 +466,14 @@
                 "</config>"
             "</edit-config>"
         "</rpc>");
-    free(str);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     /* wrong namespace, element name, whatever... */
     /* TODO */
 
+    lyp_free(out, NULL, 1);
+
     *state = NULL;
 }
 
@@ -472,6 +487,9 @@
     struct lyd_node *tree, *op;
     const struct lyd_node *node;
 
+    struct lyp_out *out;
+    assert_non_null(out = lyp_new_memory(&str, 0));
+
     data =
         "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" msgid=\"25\" custom-attr=\"val\">"
             "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">"
@@ -496,7 +514,7 @@
     assert_string_equal(((struct lyd_node_opaq *)node)->name, "action");
     assert_null(((struct lyd_node_opaq *)node)->attr);
 
-    lyd_print_mem(&str, tree, LYD_XML, 0);
+    lyd_print(out, tree, LYD_XML, 0);
     assert_string_equal(str,
         "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" msgid=\"25\" custom-attr=\"val\">"
             "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">"
@@ -507,12 +525,14 @@
                 "</c>"
             "</action>"
         "</rpc>");
-    free(str);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     /* wrong namespace, element name, whatever... */
     /* TODO */
 
+    lyp_free(out, NULL, 1);
+
     *state = NULL;
 }
 
@@ -526,6 +546,9 @@
     struct lyd_node *tree, *ntf;
     const struct lyd_node *node;
 
+    struct lyp_out *out;
+    assert_non_null(out = lyp_new_memory(&str, 0));
+
     data =
         "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">"
             "<eventTime>2037-07-08T00:01:00Z</eventTime>"
@@ -553,9 +576,9 @@
     assert_non_null(node->schema);
     assert_string_equal(node->schema->name, "c");
 
-    lyd_print_mem(&str, tree, LYD_XML, 0);
+    lyd_print(out, tree, LYD_XML, 0);
     assert_string_equal(str, data);
-    free(str);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     /* top-level notif without envelope */
@@ -568,14 +591,16 @@
     assert_non_null(tree);
     assert_ptr_equal(ntf, tree);
 
-    lyd_print_mem(&str, tree, LYD_XML, 0);
+    lyd_print(out, tree, LYD_XML, 0);
     assert_string_equal(str, data);
-    free(str);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     /* wrong namespace, element name, whatever... */
     /* TODO */
 
+    lyp_free(out, NULL, 1);
+
     *state = NULL;
 }
 
@@ -589,6 +614,9 @@
     struct lyd_node *request, *tree, *op;
     const struct lyd_node *node;
 
+    struct lyp_out *out;
+    assert_non_null(out = lyp_new_memory(&str, 0));
+
     data =
         "<c xmlns=\"urn:tests:a\">"
             "<act>"
@@ -619,15 +647,17 @@
     assert_string_equal(node->schema->name, "c");
 
     /* TODO print only rpc-reply node and then output subtree */
-    lyd_print_mem(&str, lyd_node_children(op), LYD_XML, 0);
+    lyd_print(out, lyd_node_children(op), LYD_XML, 0);
     assert_string_equal(str,
         "<al xmlns=\"urn:tests:a\">25</al>");
-    free(str);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     /* wrong namespace, element name, whatever... */
     /* TODO */
 
+    lyp_free(out, NULL, 1);
+
     *state = NULL;
 }
 
diff --git a/tests/utests/data/test_printer_xml.c b/tests/utests/data/test_printer_xml.c
index f6cca7b..084ac2c 100644
--- a/tests/utests/data/test_printer_xml.c
+++ b/tests/utests/data/test_printer_xml.c
@@ -176,18 +176,20 @@
     const char *result;
     char *printed;
     ssize_t len;
+    struct lyp_out *out;
 
     s->func = test_leaf;
+    assert_non_null(out = lyp_new_memory(&printed, 0));
 
     data = "<int8 xmlns=\"urn:tests:types\">\n 15 \t\n  </int8>";
     result = "<int8 xmlns=\"urn:tests:types\">15</int8>";
     assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, result);
-    free(printed);
     lyd_free_all(tree);
 
+    lyp_free(out, NULL, 1);
     s->func = NULL;
 }
 
@@ -199,25 +201,27 @@
     const char *data;
     char *printed;
     ssize_t len;
+    struct lyp_out *out;
 
     s->func = test_anydata;
+    assert_non_null(out = lyp_new_memory(&printed, 0));
 
     data = "<any xmlns=\"urn:tests:types\"><somexml xmlns:x=\"url:x\" xmlns=\"example.com\"><x:x/></somexml></any>";
     assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     /* canonized */
     data = "<any xmlns=\"urn:tests:types\"><somexml xmlns=\"example.com\"><x xmlns=\"url:x\"/></somexml></any>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     data = "<any xmlns=\"urn:tests:types\"/>";
     assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
     lyd_free_all(tree);
 
     data =
@@ -236,7 +240,7 @@
     assert_string_equal(((struct lyd_node_any *)tree)->value.tree->schema->name, "cont");
     /* but its children not */
     assert_null(((struct lyd_node_inner *)(((struct lyd_node_any *)tree)->value.tree))->child->schema);
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     /* canonized */
     data =
@@ -249,9 +253,11 @@
             "</cont>"
         "</any>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
+
     lyd_free_all(tree);
 
+    lyp_free(out, NULL, 1);
     s->func = NULL;
 }
 
@@ -263,39 +269,42 @@
     const char *data;
     char *printed;
     ssize_t len;
+    struct lyp_out *out;
 
     s->func = test_defaults;
 
+    assert_non_null(out = lyp_new_memory(&printed, 0));
+
     /* standard default value */
     data = "<c xmlns=\"urn:defaults\">aa</c>";
     assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_TRIM)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_TRIM)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL)) >= 0);
     assert_int_equal(len, strlen(printed));
     data = "<c xmlns=\"urn:defaults\">aa</c><a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL_TAG)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL_TAG)) >= 0);
     assert_int_equal(len, strlen(printed));
     data = "<c xmlns=\"urn:defaults\">aa</c>"
         "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\""
         " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG)) >= 0);
     assert_int_equal(len, strlen(printed));
     data = "<c xmlns=\"urn:defaults\">aa</c>"
         "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\""
         " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
     lyd_free_all(tree);
 
@@ -303,25 +312,25 @@
     data = "<c xmlns=\"urn:defaults\">aa</c><a xmlns=\"urn:defaults\">/d:b</a>";
     assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_TRIM)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_TRIM)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL_TAG)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL_TAG)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
     lyd_free_all(tree);
 
@@ -329,34 +338,35 @@
     data = "<c xmlns=\"urn:defaults\">aa</c><a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b>";
     assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_TRIM)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_TRIM)) >= 0);
     assert_int_equal(len, strlen(printed));
     data = "<c xmlns=\"urn:defaults\">aa</c><b xmlns=\"urn:defaults\">val</b>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL)) >= 0);
     assert_int_equal(len, strlen(printed));
     data = "<c xmlns=\"urn:defaults\">aa</c><a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL_TAG)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_ALL_TAG)) >= 0);
     assert_int_equal(len, strlen(printed));
     data = "<c xmlns=\"urn:defaults\">aa</c>"
         "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\""
         " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>"
         "<b xmlns=\"urn:defaults\">val</b>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
-    assert_true((len = lyd_print_mem(&printed, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG)) >= 0);
+    assert_true((len = lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG)) >= 0);
     assert_int_equal(len, strlen(printed));
     data = "<c xmlns=\"urn:defaults\">aa</c><a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b>";
     assert_string_equal(printed, data);
-    free(printed);
+    lyp_out_reset(out);
 
     lyd_free_all(tree);
+    lyp_free(out, NULL, 1);
 
     s->func = NULL;
 }
@@ -374,23 +384,25 @@
     const char *reply, *result;
     char *printed;
     ssize_t len;
+    struct lyp_out *out;
 
     s->func = test_rpc;
+    assert_non_null(out = lyp_new_memory(&printed, 0));
 
     request = "<sum xmlns=\"urn:tests:types\"><x>10</x><y>20</y></sum>";
     reply = "<result xmlns=\"urn:tests:types\">30</result>";
     result = "<sum xmlns=\"urn:tests:types\"><result>30</result></sum>";
     assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL));
-    assert_true((len = lyd_print_mem(&printed, tree1, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree1, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, request);
-    free(printed);
+    lyp_out_reset(out);
     assert_non_null(trees = lyd_trees_new(1, tree1));
     assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees));
-    assert_true((len = lyd_print_mem(&printed, tree2, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree2, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, result);
-    free(printed);
+    lyp_out_reset(out);
     lyd_trees_free(trees, 0);
     lyd_free_all(tree1);
     lyd_free_all(tree2);
@@ -400,16 +412,16 @@
     reply = "";
     result = "<sum xmlns=\"urn:tests:types\"/>";
     assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL));
-    assert_true((len = lyd_print_mem(&printed, tree1, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree1, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, request);
-    free(printed);
+    lyp_out_reset(out);
     assert_non_null(trees = lyd_trees_new(1, tree1));
     assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees));
-    assert_true((len = lyd_print_mem(&printed, tree2, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree2, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, result);
-    free(printed);
+    lyp_out_reset(out);
     lyd_trees_free(trees, 0);
     lyd_free_all(tree1);
     lyd_free_all(tree2);
@@ -424,20 +436,21 @@
     reply = "<b xmlns=\"urn:tests:types\">test-reply</b>";
     result = "<cont xmlns=\"urn:tests:types\"><listtarget><id>10</id><test><b>test-reply</b></test></listtarget></cont>";;
     assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL));
-    assert_true((len = lyd_print_mem(&printed, tree1, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree1, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, request);
-    free(printed);
+    lyp_out_reset(out);
     assert_non_null(trees = lyd_trees_new(1, tree1));
     assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees));
-    assert_true((len = lyd_print_mem(&printed, tree2, LYD_XML, 0)) >= 0);
+    assert_true((len = lyd_print(out, tree2, LYD_XML, 0)) >= 0);
     assert_int_equal(len, strlen(printed));
     assert_string_equal(printed, result);
-    free(printed);
+    lyp_out_reset(out);
     lyd_trees_free(trees, 0);
     lyd_free_all(tree1);
     lyd_free_all(tree2);
 
+    lyp_free(out, NULL, 1);
     s->func = NULL;
 }
 
diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c
index 52d59ec..16d742a 100644
--- a/tests/utests/data/test_validation.c
+++ b/tests/utests/data/test_validation.c
@@ -1019,13 +1019,16 @@
     struct lyd_node *tree, *node;
     const struct lys_module *mod = ly_ctx_get_module_latest(ctx, "f");
 
+    struct lyp_out *out;
+    assert_non_null(out = lyp_new_memory(&str, 0));
+
     /* get defaults */
     tree = NULL;
     assert_int_equal(lyd_validate_modules(&tree, &mod, 1, 0), LY_SUCCESS);
     assert_non_null(tree);
 
     /* check all defaults exist */
-    lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+    lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
     assert_string_equal(str,
         "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>"
         "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>"
@@ -1041,7 +1044,7 @@
             "<ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>"
             "<ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>"
         "</cont>");
-    free(str);
+    lyp_out_reset(out);
 
     /* create another explicit case and validate */
     node = lyd_new_term(NULL, mod, "l", "value");
@@ -1050,7 +1053,7 @@
     assert_int_equal(lyd_validate(&tree, ctx, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
 
     /* check data tree */
-    lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+    lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
     assert_string_equal(str,
         "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>"
         "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>"
@@ -1064,7 +1067,7 @@
             "<ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>"
         "</cont>"
         "<l xmlns=\"urn:tests:f\">value</l>");
-    free(str);
+    lyp_out_reset(out);
 
     /* create explicit leaf-list and leaf and validate */
     node = lyd_new_term(NULL, mod, "d", "15");
@@ -1076,7 +1079,7 @@
     assert_int_equal(lyd_validate(&tree, ctx, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
 
     /* check data tree */
-    lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+    lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
     assert_string_equal(str,
         "<cont xmlns=\"urn:tests:f\">"
             "<ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>"
@@ -1089,7 +1092,7 @@
         "<l xmlns=\"urn:tests:f\">value</l>"
         "<d xmlns=\"urn:tests:f\">15</d>"
         "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
-    free(str);
+    lyp_out_reset(out);
 
     /* create first explicit container, which should become implicit */
     node = lyd_new_inner(NULL, mod, "cont");
@@ -1099,7 +1102,7 @@
     assert_int_equal(lyd_validate(&tree, ctx, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
 
     /* check data tree */
-    lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+    lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
     assert_string_equal(str,
         "<cont xmlns=\"urn:tests:f\">"
             "<ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>"
@@ -1112,7 +1115,7 @@
         "<l xmlns=\"urn:tests:f\">value</l>"
         "<d xmlns=\"urn:tests:f\">15</d>"
         "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
-    free(str);
+    lyp_out_reset(out);
 
     /* create second explicit container, which should become implicit, so the first tree node should be removed */
     node = lyd_new_inner(NULL, mod, "cont");
@@ -1121,7 +1124,7 @@
     assert_int_equal(lyd_validate(&tree, ctx, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
 
     /* check data tree */
-    lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+    lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
     assert_string_equal(str,
         "<cont xmlns=\"urn:tests:f\">"
             "<ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>"
@@ -1134,7 +1137,7 @@
         "<l xmlns=\"urn:tests:f\">value</l>"
         "<d xmlns=\"urn:tests:f\">15</d>"
         "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
-    free(str);
+    lyp_out_reset(out);
 
     /* similar changes for nested defaults */
     assert_non_null(lyd_new_term(tree, NULL, "ll1", "def3"));
@@ -1143,7 +1146,7 @@
     assert_int_equal(lyd_validate(&tree, ctx, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
 
     /* check data tree */
-    lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+    lyd_print(out, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
     assert_string_equal(str,
         "<cont xmlns=\"urn:tests:f\">"
             "<ll1>def3</ll1>"
@@ -1153,9 +1156,10 @@
         "<l xmlns=\"urn:tests:f\">value</l>"
         "<d xmlns=\"urn:tests:f\">15</d>"
         "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
-    free(str);
+    lyp_out_reset(out);
 
     lyd_free_siblings(tree);
+    lyp_free(out, NULL, 1);
 
     *state = NULL;
 }
diff --git a/tests/utests/schema/test_printer_yang.c b/tests/utests/schema/test_printer_yang.c
index 1f3f749..93e3aeb 100644
--- a/tests/utests/schema/test_printer_yang.c
+++ b/tests/utests/schema/test_printer_yang.c
@@ -131,16 +131,19 @@
             "    \"some reference\";\n"
             "}\n";
     char *printed;
+    struct lyp_out *out;
+    size_t size = 0;
 
+    assert_non_null(out = lyp_new_memory(&printed, 0));
     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_int_equal(strlen(orig), size = lys_print(out, 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));
+    lyp_out_reset(out);
+    assert_int_equal(strlen(compiled), lys_print(out, mod, LYS_OUT_YANG_COMPILED, 0, 0));
     assert_string_equal(printed, compiled);
-    free(printed);
+    lyp_out_reset(out);
 
     orig = "module b {\n"
             "  yang-version 1.1;\n"
@@ -184,12 +187,12 @@
             "  }\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_int_equal(strlen(orig), lys_print(out, 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));
+    lyp_out_reset(out);
+    assert_int_equal(strlen(compiled), lys_print(out, mod, LYS_OUT_YANG_COMPILED, 0, 0));
     assert_string_equal(printed, compiled);
-    free(printed);
+    lyp_out_reset(out);
 
     orig = compiled ="module c {\n"
             "  yang-version 1.1;\n"
@@ -209,14 +212,15 @@
             "  }\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_int_equal(strlen(orig), lys_print(out, 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));
+    lyp_out_reset(out);
+    assert_int_equal(strlen(compiled), lys_print(out, mod, LYS_OUT_YANG, 0, 0));
     assert_string_equal(printed, compiled);
-    free(printed);
+    /* missing free(printed); which is done in the following lyp_free() */
 
     *state = NULL;
+    lyp_free(out, NULL, 1);
     ly_ctx_destroy(ctx, NULL);
 }
 
diff --git a/tests/utests/schema/test_printer_yin.c b/tests/utests/schema/test_printer_yin.c
index 2ffa611..5656795 100644
--- a/tests/utests/schema/test_printer_yin.c
+++ b/tests/utests/schema/test_printer_yin.c
@@ -578,20 +578,27 @@
             "  </rpc>\n"
             "</module>\n";
 
-    char * printed;
+    char *printed;
+    struct lyp_out *out;
+
+    assert_non_null(out = lyp_new_memory(&printed, 0));
     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(ori_res), lys_print_mem(&printed, mod, LYS_OUT_YIN, 0, 0));
+    assert_int_equal(strlen(ori_res), lys_print(out, mod, LYS_OUT_YIN, 0, 0));
     assert_string_equal(printed, ori_res);
-    free(printed);
+
     /*
+    lyp_memory_clean(out);
     assert_int_equal(strlen(compiled), lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0, 0));
     assert_string_equal(printed, compiled);
-    free(printed);
     */
 
+    /* note that the printed is freed here, so it must not be freed via lyp_free()! */
+    free(printed);
+
     *state = NULL;
+    lyp_free(out, NULL, 0);
     ly_ctx_destroy(ctx, NULL);
 }
 
diff --git a/tools/lint/commands.c b/tools/lint/commands.c
index 0ef960e..d82e6e9 100644
--- a/tools/lint/commands.c
+++ b/tools/lint/commands.c
@@ -58,7 +58,7 @@
 void
 cmd_print_help(void)
 {
-    printf("print [-f (yang | yin | tree [<tree-options>] | info [-P <info-path>] | jsons)] [-o <output-file>]"
+    printf("print [-f (yang | yin | tree [<tree-options>] | info [-P <info-path>] [-(-s)ingle-node])] [-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");
@@ -68,10 +68,7 @@
     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("\tinfo-path:\t<schema-path> | identity/<identity-name> | feature/<feature-name>\n");
     printf("\n");
     printf("\tschema-path:\t( /<module-name>:<node-identifier> )+\n");
 }
@@ -344,14 +341,13 @@
 int
 cmd_print(const char *arg)
 {
-    int c, argc, option_index, ret = 1, tree_ll = 0, tree_opts = 0, compiled = 0;
+    int c, argc, option_index, ret = 1, tree_ll = 0, output_opts = 0;
     char **argv = NULL, *ptr, *model_name, *revision;
-    const char *out_path = NULL;
+    const char *out_path = NULL, *target_path = NULL;
     const struct lys_module *module;
     LYS_OUTFORMAT format = LYS_OUT_TREE;
-    FILE *output = stdout;
+    struct lyp_out *out = NULL;
     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'},
@@ -360,7 +356,10 @@
         {"tree-print-uses", no_argument, 0, 'u'},
         {"tree-no-leafref-target", no_argument, 0, 'n'},
         {"tree-path", required_argument, 0, 'P'},
+#endif
         {"info-path", required_argument, 0, 'P'},
+        {"single-node", no_argument, 0, 's'},
+#if 0
         {"tree-line-length", required_argument, 0, 'L'},
 #endif
         {NULL, 0, 0, 0}
@@ -385,15 +384,12 @@
     optind = 0;
     while (1) {
         option_index = 0;
-        c = getopt_long(argc, argv, "chf:go:guP:L:", long_options, &option_index);
+        c = getopt_long(argc, argv, "chf:go:guP:sL:", long_options, &option_index);
         if (c == -1) {
             break;
         }
 
         switch (c) {
-        case 'c':
-            compiled = 1;
-            break;
         case 'h':
             cmd_print_help();
             ret = 0;
@@ -408,12 +404,10 @@
                 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;
+                output_opts |= LYS_OUTOPT_TREE_RFC;
 #endif
+            } else if (!strcmp(optarg, "info")) {
+                format = LYS_OUT_YANG_COMPILED;
             } else {
                 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
                 goto cleanup;
@@ -428,17 +422,22 @@
             break;
 #if 0
         case 'g':
-            tree_opts |= LYS_OUTOPT_TREE_GROUPING;
+            output_opts |= LYS_OUTOPT_TREE_GROUPING;
             break;
         case 'u':
-            tree_opts |= LYS_OUTOPT_TREE_USES;
+            output_opts |= LYS_OUTOPT_TREE_USES;
             break;
         case 'n':
-            tree_opts |= LYS_OUTOPT_TREE_NO_LEAFREF;
+            output_opts |= LYS_OUTOPT_TREE_NO_LEAFREF;
             break;
+#endif
         case 'P':
             target_path = optarg;
             break;
+        case 's':
+            output_opts |= LYS_OUTPUT_NO_SUBSTMT;
+            break;
+#if 0
         case 'L':
             tree_ll = atoi(optarg);
             break;
@@ -450,75 +449,76 @@
     }
 
     /* file name */
-    if (optind == argc) {
+    if (optind == argc && !target_path) {
         fprintf(stderr, "Missing the module name.\n");
         goto cleanup;
     }
 
-    /* compiled format */
-    if (compiled) {
-        if (format == LYS_OUT_YANG) {
-            format = LYS_OUT_YANG_COMPILED;
-        } else {
-            fprintf(stderr, "warning: --compiled option takes effect only in case of printing schemas in YANG format.\n");
-        }
-    }
 #if 0
     /* tree fromat with or without gropings */
-    if ((tree_opts || tree_ll) && format != LYS_OUT_TREE) {
+    if ((output_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 (!target_path) {
+        /* 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);
-    }
+        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));
+        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;
         }
     }
 
-    ret = lys_print_file(output, module, format, tree_ll, tree_opts);
-    if (format == LYS_OUT_JSON) {
-        fputs("\n", output);
+    if (out_path) {
+        out = lyp_new_filepath(out_path);
+    } else {
+        out = lyp_new_file(stdout);
+    }
+    if (!out) {
+        fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
+        goto cleanup;
+    }
+
+    if (target_path) {
+        const struct lysc_node *node = lys_find_node(ctx, NULL, target_path);
+        if (node) {
+            ret = lys_print_node(out, node, format, tree_ll, output_opts);
+        } else {
+            fprintf(stderr, "The requested schema node \"%s\" does not exists.\n", target_path);
+        }
+    } else {
+        ret = lys_print(out, module, format, tree_ll, output_opts);
     }
 
 cleanup:
     free(*argv);
     free(argv);
 
-    if (output && (output != stdout)) {
-        fclose(output);
-    }
+    lyp_free(out, NULL, out_path ? 1 : 0);
 
     return ret;
 }
@@ -698,8 +698,9 @@
     const char *out_path = NULL;
     struct lyd_node *data = NULL;
     struct lyd_node *tree = NULL;
+    const struct lyd_node **trees = NULL;
     LYD_FORMAT outformat = 0;
-    FILE *output = stdout;
+    struct lyp_out *out = NULL;
     static struct option long_options[] = {
         {"defaults", required_argument, 0, 'd'},
         {"help", no_argument, 0, 'h'},
@@ -834,15 +835,17 @@
     }
 
     if (out_path) {
-        output = fopen(out_path, "w");
-        if (!output) {
-            fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
-            goto cleanup;
-        }
+        out = lyp_new_filepath(out_path);
+    } else {
+        out = lyp_new_file(stdout);
+    }
+    if (!out) {
+        fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
+        goto cleanup;
     }
 
     if (outformat) {
-        lyd_print_file(output, data, outformat, LYDP_WITHSIBLINGS | LYDP_FORMAT | printopt);
+        lyd_print(out, data, outformat, LYDP_WITHSIBLINGS | LYDP_FORMAT | printopt);
     }
 
     ret = 0;
@@ -851,9 +854,7 @@
     free(*argv);
     free(argv);
 
-    if (output && (output != stdout)) {
-        fclose(output);
-    }
+    lyp_free(out, NULL, out_path ? 1 : 0);
 
     lyd_free_all(data);
 
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 97b5e4e..7a16b62 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -39,7 +39,7 @@
 help(int shortout)
 {
     fprintf(stdout, "Usage:\n");
-    fprintf(stdout, "    yanglint [options] [-f { yang | yin | tree | tree-rfc | jsons}] <file>...\n");
+    fprintf(stdout, "    yanglint [options] [-f { yang | yin | tree | tree-rfc | info}] <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");
@@ -71,7 +71,7 @@
         "                        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"
+        "                        yang, yin, tree and info 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"
@@ -125,7 +125,12 @@
         "                        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"
+        "                        can often appear).\n\n"
+        "Info output specific options:\n"
+        "  -P INFOPATH, --info-path=INFOPATH\n"
+        "                        - Schema path with full module names used as node's prefixes, the path identify the root\n"
+        "                          node of the subtree to print information about.\n"
+        "  --single-node         - Print information about a single node instead of a subtree."
         "\n");
 }
 
@@ -253,7 +258,7 @@
 main_ni(int argc, char* argv[])
 {
     int ret = EXIT_FAILURE;
-    int opt, opt_index = 0, i, featsize = 0, compiled = 0;
+    int opt, opt_index = 0, i, featsize = 0;
     struct option options[] = {
 #if 0
         {"auto",             no_argument,       NULL, 'a'},
@@ -268,7 +273,6 @@
         {"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'},
@@ -281,10 +285,12 @@
 #endif
         {"output",           required_argument, NULL, 'o'},
         {"path",             required_argument, NULL, 'p'},
+        {"info-path",        required_argument, NULL, 'P'},
 #if 0
         {"running",          required_argument, NULL, 'r'},
         {"operational",      required_argument, NULL, 'O'},
 #endif
+        {"single-node",      no_argument,       NULL, 'q'},
         {"strict",           no_argument,       NULL, 's'},
         {"type",             required_argument, NULL, 't'},
         {"version",          no_argument,       NULL, 'v'},
@@ -295,7 +301,7 @@
         {NULL,               required_argument, NULL, 'y'},
         {NULL,               0,                 NULL, 0}
     };
-    FILE *out = stdout;
+    struct lyp_out *out = NULL;
     struct ly_ctx *ctx = NULL;
     const struct lys_module *mod;
     LYS_OUTFORMAT outformat_s = 0;
@@ -359,9 +365,6 @@
             }
             break;
 #endif
-        case 'c':
-            compiled = 1;
-            break;
         case 'f':
             if (!strcasecmp(optarg, "yang")) {
                 outformat_s = LYS_OUT_YANG;
@@ -378,11 +381,9 @@
             } else if (!strcasecmp(optarg, "yin")) {
                 outformat_s = LYS_OUT_YIN;
                 outformat_d = 0;
-#if 0
-            } else if (!strcasecmp(optarg, "jsons")) {
-                outformat_s = LYS_OUT_JSON;
+            } else if (!strcasecmp(optarg, "info")) {
+                outformat_s = LYS_OUT_YANG_COMPILED;
                 outformat_d = 0;
-#endif
             } else if (!strcasecmp(optarg, "xml")) {
                 outformat_s = 0;
                 outformat_d = LYD_XML;
@@ -428,9 +429,11 @@
         case 'n':
             outoptions_s |= LYS_OUTOPT_TREE_NO_LEAFREF;
             break;
+#endif
         case 'P':
             outtarget_s = optarg;
             break;
+#if 0
         case 'L':
             outline_length_s = atoi(optarg);
             break;
@@ -468,13 +471,17 @@
             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;
+            if (out) {
+                if (lyp_filepath(out, optarg) != NULL) {
+                    fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
+                    goto cleanup;
+                }
+            } else {
+                out = lyp_new_filepath(optarg);
+                if (!out) {
+                    fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
+                    goto cleanup;
+                }
             }
             break;
         case 'p':
@@ -606,13 +613,6 @@
         fprintf(stderr, "yanglint error: missing <file> to process\n");
         goto cleanup;
     }
-    if (compiled) {
-        if (outformat_s != LYS_OUT_YANG) {
-            fprintf(stderr, "yanglint warning: --compiled option takes effect only in case of printing schemas in YANG format.\n");
-        } else {
-            outformat_s = LYS_OUT_YANG_COMPILED;
-        }
-    }
     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");
@@ -765,24 +765,19 @@
 
     /* 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);
-                }
+        if (outtarget_s) {
+            const struct lysc_node *node = lys_find_node(ctx, NULL, outtarget_s);
+            if (node) {
+                lys_print_node(out, node, outformat_s, outline_length_s, outoptions_s);
+            } else {
+                fprintf(stderr, "yanglint error: The requested schema node \"%s\" does not exists.\n", outtarget_s);
             }
-            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 {
+            for (u = 0; u < mods->count; u++) {
+                if (u) {
+                    lyp_print(out, "\n");
+                }
+                lys_print(out, (struct lys_module *)mods->objs[u], outformat_s, outline_length_s, outoptions_s);
             }
         }
     } else if (data) {
@@ -1060,7 +1055,7 @@
                     }
                 }
 #endif
-                lyd_print_file(out, data_item->tree, outformat_d, LYDP_WITHSIBLINGS | LYDP_FORMAT /* TODO defaults | options_dflt */);
+                lyd_print(out, 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) {
@@ -1085,9 +1080,6 @@
     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++) {
@@ -1101,5 +1093,6 @@
     }
     ly_ctx_destroy(ctx, NULL);
 
+    lyp_free(out, NULL, 1);
     return ret;
 }