summaryrefslogtreecommitdiffstats
path: root/src/out.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/out.c788
1 files changed, 788 insertions, 0 deletions
diff --git a/src/out.c b/src/out.c
new file mode 100644
index 0000000..451fba4
--- /dev/null
+++ b/src/out.c
@@ -0,0 +1,788 @@
+/**
+ * @file out.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang output functions.
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "out.h"
+#include "out_internal.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "log.h"
+#include "ly_common.h"
+#include "metadata.h"
+#include "printer_data.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+/**
+ * @brief Align the desired size to 1 KB.
+ */
+#define REALLOC_CHUNK(NEW_SIZE) \
+ NEW_SIZE + (1024 - (NEW_SIZE % 1024))
+
+LIBYANG_API_DEF ly_bool
+lyd_node_should_print(const struct lyd_node *node, uint32_t options)
+{
+ const struct lyd_node *elem;
+
+ if (options & LYD_PRINT_WD_TRIM) {
+ /* do not print default nodes */
+ if (node->flags & LYD_DEFAULT) {
+ /* implicit default node/NP container with only default nodes */
+ return 0;
+ } else if (node->schema && (node->schema->nodetype & LYD_NODE_TERM)) {
+ if (lyd_is_default(node)) {
+ /* explicit default node */
+ return 0;
+ }
+ } else if (lysc_is_np_cont(node->schema)) {
+ if (options & LYD_PRINT_KEEPEMPTYCONT) {
+ /* explicit request to print, redundant to check */
+ return 1;
+ }
+
+ LY_LIST_FOR(lyd_child(node), elem) {
+ if (lyd_node_should_print(elem, options)) {
+ return 1;
+ }
+ }
+
+ /* NP container without any printed children (such as other NP containers with only nodes set to their default values) */
+ return 0;
+ }
+ } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER)) {
+ if (options & LYD_PRINT_KEEPEMPTYCONT) {
+ /* explicit request to print */
+ return 1;
+ }
+
+ /* avoid empty default containers */
+ LYD_TREE_DFS_BEGIN(node, elem) {
+ if ((elem != node) && lyd_node_should_print(elem, options)) {
+ return 1;
+ }
+ assert(elem->flags & LYD_DEFAULT);
+ LYD_TREE_DFS_END(node, elem)
+ }
+ return 0;
+ } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
+ /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
+ if (!(node->schema->flags & (LYS_IS_INPUT | LYS_IS_OUTPUT | LYS_IS_NOTIF)) && (node->schema->flags & LYS_CONFIG_W)) {
+ /* print only if it contains status data in its subtree */
+ LYD_TREE_DFS_BEGIN(node, elem) {
+ if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
+ if (elem->schema->flags & LYS_CONFIG_R) {
+ return 1;
+ }
+ }
+ LYD_TREE_DFS_END(node, elem)
+ }
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+LIBYANG_API_DEF ly_bool
+lyd_metadata_should_print(const struct lyd_meta *meta)
+{
+ return !lyd_meta_is_internal(meta);
+}
+
+LIBYANG_API_DEF LY_OUT_TYPE
+ly_out_type(const struct ly_out *out)
+{
+ LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
+ return out->type;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+
+ (*out)->type = LY_OUT_CALLBACK;
+ (*out)->method.clb.func = writeclb;
+ (*out)->method.clb.arg = user_data;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF ly_write_clb
+ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
+{
+ ly_write_clb prev_clb;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
+
+ prev_clb = out->method.clb.func;
+
+ if (writeclb) {
+ out->method.clb.func = writeclb;
+ }
+
+ return prev_clb;
+}
+
+LIBYANG_API_DEF void *
+ly_out_clb_arg(struct ly_out *out, void *arg)
+{
+ void *prev_arg;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
+
+ prev_arg = out->method.clb.arg;
+
+ if (arg) {
+ out->method.clb.arg = arg;
+ }
+
+ return prev_arg;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_fd(int fd, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+ (*out)->type = LY_OUT_FD;
+ (*out)->method.fd = fd;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF int
+ly_out_fd(struct ly_out *out, int fd)
+{
+ int prev_fd;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
+
+ if (out->type == LY_OUT_FDSTREAM) {
+ prev_fd = out->method.fdstream.fd;
+ } else { /* LY_OUT_FD */
+ prev_fd = out->method.fd;
+ }
+
+ if (fd != -1) {
+ /* replace output stream */
+ if (out->type == LY_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 { /* LY_OUT_FD */
+ out->method.fd = fd;
+ }
+ }
+
+ return prev_fd;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_file(FILE *f, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+
+ (*out)->type = LY_OUT_FILE;
+ (*out)->method.f = f;
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF FILE *
+ly_out_file(struct ly_out *out, FILE *f)
+{
+ FILE *prev_f;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
+
+ prev_f = out->method.f;
+
+ if (f) {
+ out->method.f = f;
+ }
+
+ return prev_f;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+
+ (*out)->type = LY_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 LY_SUCCESS;
+}
+
+char *
+ly_out_memory(struct ly_out *out, char **strp, size_t size)
+{
+ char *data;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_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;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_reset(struct ly_out *out)
+{
+ LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
+
+ switch (out->type) {
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ case LY_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;
+ }
+ if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
+ LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
+ return LY_ESYS;
+ }
+ break;
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILE:
+ case LY_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;
+ }
+ if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
+ LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
+ return LY_ESYS;
+ }
+ break;
+ case LY_OUT_MEMORY:
+ if (out->method.mem.buf && *out->method.mem.buf) {
+ memset(*out->method.mem.buf, 0, out->method.mem.len);
+ }
+ out->printed = 0;
+ out->method.mem.len = 0;
+ break;
+ case LY_OUT_CALLBACK:
+ /* nothing to do (not seekable) */
+ break;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_out_new_filepath(const char *filepath, struct ly_out **out)
+{
+ LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
+
+ *out = calloc(1, sizeof **out);
+ LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
+
+ (*out)->type = LY_OUT_FILEPATH;
+ (*out)->method.fpath.f = fopen(filepath, "wb");
+ if (!(*out)->method.fpath.f) {
+ LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
+ free(*out);
+ *out = NULL;
+ return LY_ESYS;
+ }
+ (*out)->method.fpath.filepath = strdup(filepath);
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const char *
+ly_out_filepath(struct ly_out *out, const char *filepath)
+{
+ FILE *f;
+
+ LY_CHECK_ARG_RET(NULL, out, out->type == LY_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, "wb");
+ 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;
+}
+
+LIBYANG_API_DEF void
+ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
+{
+ if (!out) {
+ return;
+ }
+
+ switch (out->type) {
+ case LY_OUT_CALLBACK:
+ if (clb_arg_destructor) {
+ clb_arg_destructor(out->method.clb.arg);
+ }
+ break;
+ case LY_OUT_FDSTREAM:
+ fclose(out->method.fdstream.f);
+ if (destroy) {
+ close(out->method.fdstream.fd);
+ }
+ break;
+ case LY_OUT_FD:
+ if (destroy) {
+ close(out->method.fd);
+ }
+ break;
+ case LY_OUT_FILE:
+ if (destroy) {
+ fclose(out->method.f);
+ }
+ break;
+ case LY_OUT_MEMORY:
+ if (destroy) {
+ free(*out->method.mem.buf);
+ }
+ break;
+ case LY_OUT_FILEPATH:
+ free(out->method.fpath.filepath);
+ fclose(out->method.fpath.f);
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ }
+
+ free(out->buffered);
+ free(out);
+}
+
+static LY_ERR
+ly_vprint_(struct ly_out *out, const char *format, va_list ap)
+{
+ LY_ERR ret;
+ int written = 0;
+ char *msg = NULL;
+
+ switch (out->type) {
+ case LY_OUT_FD:
+ written = vdprintf(out->method.fd, format, ap);
+ break;
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ written = vfprintf(out->method.f, format, ap);
+ break;
+ case LY_OUT_MEMORY:
+ if ((written = vasprintf(&msg, format, ap)) < 0) {
+ break;
+ }
+ if (out->method.mem.len + written + 1 > out->method.mem.size) {
+ *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
+ if (!*out->method.mem.buf) {
+ out->method.mem.len = 0;
+ out->method.mem.size = 0;
+ free(msg);
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->method.mem.size = out->method.mem.len + written + 1;
+ }
+ if (written) {
+ memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
+ }
+ out->method.mem.len += written;
+ (*out->method.mem.buf)[out->method.mem.len] = '\0';
+ free(msg);
+ break;
+ case LY_OUT_CALLBACK:
+ if ((written = vasprintf(&msg, format, ap)) < 0) {
+ break;
+ }
+ written = out->method.clb.func(out->method.clb.arg, msg, written);
+ free(msg);
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ if (written < 0) {
+ LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
+ written = 0;
+ ret = LY_ESYS;
+ } else {
+ if (out->type == LY_OUT_FDSTREAM) {
+ /* move the original file descriptor to the end of the output file */
+ lseek(out->method.fdstream.fd, 0, SEEK_END);
+ }
+ ret = LY_SUCCESS;
+ }
+
+ out->printed += written;
+ out->func_printed += written;
+ return ret;
+}
+
+LY_ERR
+ly_print_(struct ly_out *out, const char *format, ...)
+{
+ LY_ERR ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = ly_vprint_(out, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_print(struct ly_out *out, const char *format, ...)
+{
+ LY_ERR ret;
+ va_list ap;
+
+ out->func_printed = 0;
+
+ va_start(ap, format);
+ ret = ly_vprint_(out, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+LIBYANG_API_DEF void
+ly_print_flush(struct ly_out *out)
+{
+ switch (out->type) {
+ case LY_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 LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ fflush(out->method.f);
+ break;
+ case LY_OUT_FD:
+ fsync(out->method.fd);
+ break;
+ case LY_OUT_MEMORY:
+ case LY_OUT_CALLBACK:
+ /* nothing to do */
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ }
+
+ free(out->buffered);
+ out->buf_size = out->buf_len = 0;
+}
+
+LY_ERR
+ly_write_(struct ly_out *out, const char *buf, size_t len)
+{
+ LY_ERR ret = LY_SUCCESS;
+ size_t written = 0, new_mem_size;
+
+ if (out->hole_count) {
+ /* we are buffering data after a hole */
+ if (out->buf_len + len > out->buf_size) {
+ out->buffered = ly_realloc(out->buffered, out->buf_len + len);
+ if (!out->buffered) {
+ out->buf_len = 0;
+ out->buf_size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->buf_size = out->buf_len + len;
+ }
+
+ if (len) {
+ memcpy(&out->buffered[out->buf_len], buf, len);
+ }
+ out->buf_len += len;
+
+ out->printed += len;
+ out->func_printed += len;
+ return LY_SUCCESS;
+ }
+
+repeat:
+ switch (out->type) {
+ case LY_OUT_MEMORY:
+ new_mem_size = out->method.mem.len + len + 1;
+ if (new_mem_size > out->method.mem.size) {
+ new_mem_size = REALLOC_CHUNK(new_mem_size);
+ *out->method.mem.buf = ly_realloc(*out->method.mem.buf, new_mem_size);
+ if (!*out->method.mem.buf) {
+ out->method.mem.len = 0;
+ out->method.mem.size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->method.mem.size = new_mem_size;
+ }
+ if (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';
+
+ written = len;
+ break;
+ case LY_OUT_FD: {
+ ssize_t r;
+
+ r = write(out->method.fd, buf, len);
+ if (r < 0) {
+ ret = LY_ESYS;
+ } else {
+ written = (size_t)r;
+ }
+ break;
+ }
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ written = fwrite(buf, sizeof *buf, len, out->method.f);
+ if (written != len) {
+ ret = LY_ESYS;
+ }
+ break;
+ case LY_OUT_CALLBACK: {
+ ssize_t r;
+
+ r = out->method.clb.func(out->method.clb.arg, buf, len);
+ if (r < 0) {
+ ret = LY_ESYS;
+ } else {
+ written = (size_t)r;
+ }
+ break;
+ }
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ if (ret) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+ ret = LY_SUCCESS;
+ goto repeat;
+ }
+ LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
+ written = 0;
+ } else if (written != len) {
+ LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %" PRIu32 " from %" PRIu32 " data).", __func__,
+ (uint32_t)(len - written), (uint32_t)len);
+ ret = LY_ESYS;
+ } else {
+ if (out->type == LY_OUT_FDSTREAM) {
+ /* move the original file descriptor to the end of the output file */
+ lseek(out->method.fdstream.fd, 0, SEEK_END);
+ }
+ ret = LY_SUCCESS;
+ }
+
+ out->printed += written;
+ out->func_printed += written;
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+ly_write(struct ly_out *out, const char *buf, size_t len)
+{
+ out->func_printed = 0;
+
+ return ly_write_(out, buf, len);
+}
+
+LIBYANG_API_DEF size_t
+ly_out_printed(const struct ly_out *out)
+{
+ return out->func_printed;
+}
+
+LY_ERR
+ly_write_skip(struct ly_out *out, size_t count, size_t *position)
+{
+ switch (out->type) {
+ case LY_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.len = 0;
+ out->method.mem.size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->method.mem.size = out->method.mem.len + count;
+ }
+
+ /* save the current position */
+ *position = out->method.mem.len;
+
+ /* skip the memory */
+ out->method.mem.len += count;
+ break;
+ case LY_OUT_FD:
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ case LY_OUT_CALLBACK:
+ /* buffer the hole */
+ if (out->buf_len + count > out->buf_size) {
+ out->buffered = ly_realloc(out->buffered, out->buf_len + count);
+ if (!out->buffered) {
+ out->buf_len = 0;
+ out->buf_size = 0;
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+ out->buf_size = out->buf_len + count;
+ }
+
+ /* save the current position */
+ *position = out->buf_len;
+
+ /* skip the memory */
+ out->buf_len += count;
+
+ /* increase hole counter */
+ ++out->hole_count;
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ /* update printed bytes counter despite we actually printed just a hole */
+ out->printed += count;
+ out->func_printed += count;
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ assert(count);
+
+ switch (out->type) {
+ case LY_OUT_MEMORY:
+ /* write */
+ memcpy(&(*out->method.mem.buf)[position], buf, count);
+ break;
+ case LY_OUT_FD:
+ case LY_OUT_FDSTREAM:
+ case LY_OUT_FILEPATH:
+ case LY_OUT_FILE:
+ case LY_OUT_CALLBACK:
+ if (out->buf_len < position + count) {
+ LOGMEM(NULL);
+ return LY_EMEM;
+ }
+
+ /* write into the hole */
+ memcpy(&out->buffered[position], buf, count);
+
+ /* decrease hole counter */
+ --out->hole_count;
+
+ if (!out->hole_count) {
+ /* all holes filled, we can write the buffer,
+ * printed bytes counter is updated by ly_write_() */
+ ret = ly_write_(out, out->buffered, out->buf_len);
+ out->buf_len = 0;
+ }
+ break;
+ case LY_OUT_ERROR:
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ if (out->type == LY_OUT_FILEPATH) {
+ /* move the original file descriptor to the end of the output file */
+ lseek(out->method.fdstream.fd, 0, SEEK_END);
+ }
+ return ret;
+}