blob: 9383ef18fd2fb74d32bdb29d6572e22a99b6cfc9 [file] [log] [blame]
Radek Krejcid3ca0632019-04-16 16:54:54 +02001/**
Michal Vaskoafac7822020-10-20 14:22:26 +02002 * @file out.c
Radek Krejcid3ca0632019-04-16 16:54:54 +02003 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vaskoafac7822020-10-20 14:22:26 +02004 * @brief libyang output functions.
Radek Krejcid3ca0632019-04-16 16:54:54 +02005 *
Michal Vaskoafac7822020-10-20 14:22:26 +02006 * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
Radek Krejcid3ca0632019-04-16 16:54:54 +02007 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejcif8dc59a2020-11-25 13:47:44 +010016#define _POSIX_C_SOURCE 200809L /* strdup, vdprintf */
Radek Krejcid3ca0632019-04-16 16:54:54 +020017
Michal Vaskoafac7822020-10-20 14:22:26 +020018#include "out.h"
19#include "out_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020020
21#include <assert.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020022#include <errno.h>
23#include <stdarg.h>
Radek Krejci47fab892020-11-05 17:02:41 +010024#include <stdint.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020025#include <stdio.h>
26#include <stdlib.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020027#include <string.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020028#include <unistd.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020029
Radek Krejci535ea9f2020-05-29 16:01:05 +020030#include "common.h"
Michal Vasko5aa44c02020-06-29 11:47:02 +020031#include "compat.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020032#include "log.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020033#include "printer_data.h"
34#include "printer_internal.h"
Radek Krejci47fab892020-11-05 17:02:41 +010035#include "tree_data.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020036#include "tree_schema.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020037
Radek Krejci857189e2020-09-01 13:26:36 +020038ly_bool
Radek Krejci1deb5be2020-08-26 16:43:36 +020039ly_should_print(const struct lyd_node *node, uint32_t options)
Michal Vasko9b368d32020-02-14 13:53:31 +010040{
Michal Vaskob4fd37f2021-02-26 10:09:44 +010041 const struct lyd_node *elem;
Michal Vasko9b368d32020-02-14 13:53:31 +010042
Radek Krejci7931b192020-06-25 17:05:03 +020043 if (options & LYD_PRINT_WD_TRIM) {
Michal Vasko9b368d32020-02-14 13:53:31 +010044 /* do not print default nodes */
45 if (node->flags & LYD_DEFAULT) {
46 /* implicit default node/NP container with only default nodes */
47 return 0;
48 } else if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci19611252020-10-04 13:54:53 +020049 if (lyd_is_default(node)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010050 /* explicit default node */
51 return 0;
52 }
53 }
Michal Vasko5be03d62020-12-09 18:08:39 +010054 } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK)) {
55 /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
Michal Vaskob4fd37f2021-02-26 10:09:44 +010056 if (!(node->schema->flags & (LYS_IS_INPUT | LYS_IS_OUTPUT | LYS_IS_NOTIF)) && (node->schema->flags & LYS_CONFIG_W)) {
Michal Vasko5be03d62020-12-09 18:08:39 +010057 /* print only if it contains status data in its subtree */
58 LYD_TREE_DFS_BEGIN(node, elem) {
59 if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
60 if (elem->schema->flags & LYS_CONFIG_R) {
61 return 1;
62 }
Michal Vaskodb4f9e42020-06-01 17:29:56 +020063 }
Michal Vasko5be03d62020-12-09 18:08:39 +010064 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010065 }
Michal Vasko9b368d32020-02-14 13:53:31 +010066 }
67 return 0;
Radek Krejci7931b192020-06-25 17:05:03 +020068 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYD_PRINT_KEEPEMPTYCONT)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010069 /* avoid empty default containers */
Michal Vasko56daf732020-08-10 10:57:18 +020070 LYD_TREE_DFS_BEGIN(node, elem) {
Michal Vasko9b368d32020-02-14 13:53:31 +010071 if (elem->schema->nodetype != LYS_CONTAINER) {
72 return 1;
73 }
74 assert(elem->flags & LYD_DEFAULT);
Michal Vasko56daf732020-08-10 10:57:18 +020075 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010076 }
77 return 0;
78 }
79
80 return 1;
81}
82
Radek Krejci241f6b52020-05-21 18:13:49 +020083API LY_OUT_TYPE
84ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +010085{
Radek Krejci241f6b52020-05-21 18:13:49 +020086 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +010087 return out->type;
88}
89
Radek Krejci84ce7b12020-06-11 17:28:25 +020090API LY_ERR
Michal Vaskoce2b8742020-08-24 13:20:25 +020091ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +010092{
Radek Krejci84ce7b12020-06-11 17:28:25 +020093 LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +010094
Radek Krejci84ce7b12020-06-11 17:28:25 +020095 *out = calloc(1, sizeof **out);
96 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +010097
Radek Krejci84ce7b12020-06-11 17:28:25 +020098 (*out)->type = LY_OUT_CALLBACK;
99 (*out)->method.clb.func = writeclb;
Michal Vaskoce2b8742020-08-24 13:20:25 +0200100 (*out)->method.clb.arg = user_data;
Radek Krejcia5bba312020-01-09 15:41:20 +0100101
Radek Krejci84ce7b12020-06-11 17:28:25 +0200102 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100103}
104
Michal Vasko69730152020-10-09 16:30:07 +0200105API ly_write_clb
106ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
Radek Krejcia5bba312020-01-09 15:41:20 +0100107{
108 void *prev_clb;
109
Radek Krejci241f6b52020-05-21 18:13:49 +0200110 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100111
112 prev_clb = out->method.clb.func;
113
114 if (writeclb) {
115 out->method.clb.func = writeclb;
116 }
117
118 return prev_clb;
119}
120
121API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200122ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100123{
124 void *prev_arg;
125
Radek Krejci241f6b52020-05-21 18:13:49 +0200126 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100127
128 prev_arg = out->method.clb.arg;
129
130 if (arg) {
131 out->method.clb.arg = arg;
132 }
133
134 return prev_arg;
135}
136
Radek Krejci84ce7b12020-06-11 17:28:25 +0200137API LY_ERR
138ly_out_new_fd(int fd, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100139{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200140 LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100141
Radek Krejci84ce7b12020-06-11 17:28:25 +0200142 *out = calloc(1, sizeof **out);
143 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200144 (*out)->type = LY_OUT_FD;
145 (*out)->method.fd = fd;
Radek Krejcia5bba312020-01-09 15:41:20 +0100146
Radek Krejci84ce7b12020-06-11 17:28:25 +0200147 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100148}
149
150API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200151ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100152{
153 int prev_fd;
154
Radek Krejci241f6b52020-05-21 18:13:49 +0200155 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100156
Radek Krejci241f6b52020-05-21 18:13:49 +0200157 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100158 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200159 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100160 prev_fd = out->method.fd;
161 }
162
163 if (fd != -1) {
164 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200165 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100166 int streamfd;
167 FILE *stream;
168
169 streamfd = dup(fd);
170 if (streamfd < 0) {
171 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
172 return -1;
173 }
174 stream = fdopen(streamfd, "a");
175 if (!stream) {
176 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
177 close(streamfd);
178 return -1;
179 }
180 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
181 fclose(out->method.fdstream.f);
182 out->method.fdstream.f = stream;
183 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200184 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100185 out->method.fd = fd;
186 }
187 }
188
189 return prev_fd;
190}
191
Radek Krejci84ce7b12020-06-11 17:28:25 +0200192API LY_ERR
193ly_out_new_file(FILE *f, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100194{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200195 LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100196
Radek Krejci84ce7b12020-06-11 17:28:25 +0200197 *out = calloc(1, sizeof **out);
198 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100199
Radek Krejci84ce7b12020-06-11 17:28:25 +0200200 (*out)->type = LY_OUT_FILE;
201 (*out)->method.f = f;
Radek Krejcia5bba312020-01-09 15:41:20 +0100202
Radek Krejci84ce7b12020-06-11 17:28:25 +0200203 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100204}
205
206API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200207ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100208{
209 FILE *prev_f;
210
Radek Krejci241f6b52020-05-21 18:13:49 +0200211 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100212
213 prev_f = out->method.f;
214
215 if (f) {
216 out->method.f = f;
217 }
218
219 return prev_f;
220}
221
Radek Krejci84ce7b12020-06-11 17:28:25 +0200222API LY_ERR
223ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100224{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200225 LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100226
Radek Krejci84ce7b12020-06-11 17:28:25 +0200227 *out = calloc(1, sizeof **out);
228 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100229
Radek Krejci84ce7b12020-06-11 17:28:25 +0200230 (*out)->type = LY_OUT_MEMORY;
231 (*out)->method.mem.buf = strp;
Radek Krejcia5bba312020-01-09 15:41:20 +0100232 if (!size) {
233 /* buffer is supposed to be allocated */
234 *strp = NULL;
235 } else if (*strp) {
236 /* there is already buffer to use */
Radek Krejci84ce7b12020-06-11 17:28:25 +0200237 (*out)->method.mem.size = size;
Radek Krejcia5bba312020-01-09 15:41:20 +0100238 }
239
Radek Krejci84ce7b12020-06-11 17:28:25 +0200240 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100241}
242
243char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200244ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100245{
246 char *data;
247
Radek Krejci241f6b52020-05-21 18:13:49 +0200248 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100249
250 data = *out->method.mem.buf;
251
252 if (strp) {
253 out->method.mem.buf = strp;
254 out->method.mem.len = out->method.mem.size = 0;
255 out->printed = 0;
256 if (!size) {
257 /* buffer is supposed to be allocated */
258 *strp = NULL;
259 } else if (*strp) {
260 /* there is already buffer to use */
261 out->method.mem.size = size;
262 }
263 }
264
265 return data;
266}
267
268API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200269ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100270{
271 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
272
Michal Vaskod989ba02020-08-24 10:59:24 +0200273 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200274 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100275 LOGINT(NULL);
276 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200277 case LY_OUT_FD:
Michal Vasko69730152020-10-09 16:30:07 +0200278 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100279 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
280 return LY_ESYS;
281 }
Michal Vasko69730152020-10-09 16:30:07 +0200282 if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200283 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
284 return LY_ESYS;
285 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100286 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200287 case LY_OUT_FDSTREAM:
288 case LY_OUT_FILE:
289 case LY_OUT_FILEPATH:
Michal Vasko69730152020-10-09 16:30:07 +0200290 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100291 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
292 return LY_ESYS;
293 }
Michal Vasko69730152020-10-09 16:30:07 +0200294 if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200295 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
296 return LY_ESYS;
297 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100298 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200299 case LY_OUT_MEMORY:
Radek Krejcic5a12e12020-05-27 17:09:59 +0200300 if (out->method.mem.buf && *out->method.mem.buf) {
301 memset(*out->method.mem.buf, 0, out->method.mem.len);
302 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100303 out->printed = 0;
304 out->method.mem.len = 0;
305 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200306 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100307 /* nothing to do (not seekable) */
308 break;
309 }
310
311 return LY_SUCCESS;
312}
313
Radek Krejci84ce7b12020-06-11 17:28:25 +0200314API LY_ERR
315ly_out_new_filepath(const char *filepath, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100316{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200317 LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100318
Radek Krejci84ce7b12020-06-11 17:28:25 +0200319 *out = calloc(1, sizeof **out);
320 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100321
Radek Krejci84ce7b12020-06-11 17:28:25 +0200322 (*out)->type = LY_OUT_FILEPATH;
323 (*out)->method.fpath.f = fopen(filepath, "w");
324 if (!(*out)->method.fpath.f) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100325 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
Radek Krejcif6923e82020-07-02 16:36:53 +0200326 free(*out);
327 *out = NULL;
Radek Krejci84ce7b12020-06-11 17:28:25 +0200328 return LY_ESYS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100329 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200330 (*out)->method.fpath.filepath = strdup(filepath);
331 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100332}
333
334API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200335ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100336{
337 FILE *f;
338
Radek Krejci241f6b52020-05-21 18:13:49 +0200339 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100340
341 if (!filepath) {
342 return out->method.fpath.filepath;
343 }
344
345 /* replace filepath */
346 f = out->method.fpath.f;
347 out->method.fpath.f = fopen(filepath, "w");
348 if (!out->method.fpath.f) {
349 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
350 out->method.fpath.f = f;
Michal Vasko69730152020-10-09 16:30:07 +0200351 return (void *)-1;
Radek Krejcia5bba312020-01-09 15:41:20 +0100352 }
353 fclose(f);
354 free(out->method.fpath.filepath);
355 out->method.fpath.filepath = strdup(filepath);
356
357 return NULL;
358}
359
360API void
Radek Krejci857189e2020-09-01 13:26:36 +0200361ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100362{
363 if (!out) {
364 return;
365 }
366
367 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200368 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100369 if (clb_arg_destructor) {
370 clb_arg_destructor(out->method.clb.arg);
371 }
372 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200373 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100374 fclose(out->method.fdstream.f);
375 if (destroy) {
376 close(out->method.fdstream.fd);
377 }
378 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200379 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100380 if (destroy) {
381 close(out->method.fd);
382 }
383 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200384 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100385 if (destroy) {
386 fclose(out->method.f);
387 }
388 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200389 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100390 if (destroy) {
391 free(*out->method.mem.buf);
392 }
393 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200394 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100395 free(out->method.fpath.filepath);
Radek Krejci2aae3752020-05-27 18:16:30 +0200396 fclose(out->method.fpath.f);
Radek Krejcia5bba312020-01-09 15:41:20 +0100397 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200398 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100399 LOGINT(NULL);
400 }
Michal Vasko159b8872020-11-18 18:14:16 +0100401
402 free(out->buffered);
Radek Krejcia5bba312020-01-09 15:41:20 +0100403 free(out);
404}
405
Michal Vasko5233e962020-08-14 14:26:20 +0200406static LY_ERR
407ly_vprint_(struct ly_out *out, const char *format, va_list ap)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200408{
Michal Vasko5233e962020-08-14 14:26:20 +0200409 LY_ERR ret;
410 int written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200411 char *msg = NULL, *aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200412
413 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200414 case LY_OUT_FD:
Michal Vasko5233e962020-08-14 14:26:20 +0200415 written = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200416 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200417 case LY_OUT_FDSTREAM:
418 case LY_OUT_FILEPATH:
419 case LY_OUT_FILE:
Michal Vasko5233e962020-08-14 14:26:20 +0200420 written = vfprintf(out->method.f, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200421 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200422 case LY_OUT_MEMORY:
Michal Vasko5233e962020-08-14 14:26:20 +0200423 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200424 break;
425 }
Michal Vasko5233e962020-08-14 14:26:20 +0200426 if (out->method.mem.len + written + 1 > out->method.mem.size) {
427 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200428 if (!aux) {
429 out->method.mem.buf = NULL;
430 out->method.mem.len = 0;
431 out->method.mem.size = 0;
432 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200433 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200434 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100435 *out->method.mem.buf = aux;
Michal Vasko5233e962020-08-14 14:26:20 +0200436 out->method.mem.size = out->method.mem.len + written + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200437 }
Michal Vasko5233e962020-08-14 14:26:20 +0200438 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
439 out->method.mem.len += written;
Radek Krejcia5bba312020-01-09 15:41:20 +0100440 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200441 free(msg);
442 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200443 case LY_OUT_CALLBACK:
Michal Vasko5233e962020-08-14 14:26:20 +0200444 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200445 break;
446 }
Michal Vasko5233e962020-08-14 14:26:20 +0200447 written = out->method.clb.func(out->method.clb.arg, msg, written);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200448 free(msg);
449 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200450 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100451 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200452 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200453 }
454
Michal Vasko5233e962020-08-14 14:26:20 +0200455 if (written < 0) {
456 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
457 written = 0;
458 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200459 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200460 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100461 /* move the original file descriptor to the end of the output file */
462 lseek(out->method.fdstream.fd, 0, SEEK_END);
463 }
Michal Vasko5233e962020-08-14 14:26:20 +0200464 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200465 }
Michal Vasko5233e962020-08-14 14:26:20 +0200466
467 out->printed += written;
468 out->func_printed += written;
469 return ret;
470}
471
472LY_ERR
473ly_print_(struct ly_out *out, const char *format, ...)
474{
475 LY_ERR ret;
476 va_list ap;
477
478 va_start(ap, format);
479 ret = ly_vprint_(out, format, ap);
480 va_end(ap);
481
482 return ret;
483}
484
485API LY_ERR
486ly_print(struct ly_out *out, const char *format, ...)
487{
488 LY_ERR ret;
489 va_list ap;
490
491 out->func_printed = 0;
492
493 va_start(ap, format);
494 ret = ly_vprint_(out, format, ap);
495 va_end(ap);
496
497 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200498}
499
Radek Krejci2aae3752020-05-27 18:16:30 +0200500API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200501ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200502{
503 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200504 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100505 /* move the original file descriptor to the end of the output file */
506 lseek(out->method.fdstream.fd, 0, SEEK_END);
507 fflush(out->method.fdstream.f);
508 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200509 case LY_OUT_FILEPATH:
510 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200511 fflush(out->method.f);
512 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200513 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200514 fsync(out->method.fd);
515 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200516 case LY_OUT_MEMORY:
517 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200518 /* nothing to do */
519 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200520 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100521 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200522 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200523
524 free(out->buffered);
525 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200526}
527
Michal Vasko5233e962020-08-14 14:26:20 +0200528LY_ERR
529ly_write_(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200530{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200531 LY_ERR ret = LY_SUCCESS;
532 size_t written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200533
534 if (out->hole_count) {
535 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200536 if (out->buf_len + len > out->buf_size) {
537 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200538 if (!out->buffered) {
539 out->buf_len = 0;
540 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200541 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200542 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200543 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200544 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200545 }
546
Radek Krejcie7b95092019-05-15 11:03:07 +0200547 memcpy(&out->buffered[out->buf_len], buf, len);
548 out->buf_len += len;
Michal Vasko5233e962020-08-14 14:26:20 +0200549
550 out->printed += len;
551 out->func_printed += len;
552 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200553 }
554
Radek Krejci897ad2e2019-04-29 16:43:07 +0200555repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200556 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200557 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200558 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100559 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
560 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200561 out->method.mem.len = 0;
562 out->method.mem.size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200563 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200564 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200565 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200566 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200567 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100568 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
Radek Krejcie7b95092019-05-15 11:03:07 +0200569 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100570 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200571
Michal Vasko5233e962020-08-14 14:26:20 +0200572 written = len;
573 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200574 case LY_OUT_FD: {
575 ssize_t r;
576 r = write(out->method.fd, buf, len);
577 if (r < 0) {
578 ret = LY_ESYS;
579 } else {
580 written = (size_t)r;
581 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200582 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200583 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200584 case LY_OUT_FDSTREAM:
585 case LY_OUT_FILEPATH:
586 case LY_OUT_FILE:
Michal Vasko63f3d842020-07-08 10:10:14 +0200587 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200588 if (written != len) {
589 ret = LY_ESYS;
590 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200591 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200592 case LY_OUT_CALLBACK: {
593 ssize_t r;
594 r = out->method.clb.func(out->method.clb.arg, buf, len);
595 if (r < 0) {
596 ret = LY_ESYS;
597 } else {
598 written = (size_t)r;
599 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200600 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200601 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200602 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100603 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200604 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200605 }
606
Radek Krejci1deb5be2020-08-26 16:43:36 +0200607 if (ret) {
Michal Vasko69730152020-10-09 16:30:07 +0200608 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200609 ret = LY_SUCCESS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200610 goto repeat;
611 }
Michal Vasko5233e962020-08-14 14:26:20 +0200612 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
613 written = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200614 } else if ((size_t)written != len) {
Michal Vasko5233e962020-08-14 14:26:20 +0200615 LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
Michal Vasko69730152020-10-09 16:30:07 +0200616 len - (size_t)written, len);
Michal Vasko5233e962020-08-14 14:26:20 +0200617 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200618 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200619 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100620 /* move the original file descriptor to the end of the output file */
621 lseek(out->method.fdstream.fd, 0, SEEK_END);
622 }
Michal Vasko5233e962020-08-14 14:26:20 +0200623 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200624 }
Michal Vasko5233e962020-08-14 14:26:20 +0200625
626 out->printed += written;
627 out->func_printed += written;
628 return ret;
629}
630
631API LY_ERR
632ly_write(struct ly_out *out, const char *buf, size_t len)
633{
634 out->func_printed = 0;
635
636 return ly_write_(out, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200637}
638
Michal Vasko63f3d842020-07-08 10:10:14 +0200639API size_t
640ly_out_printed(const struct ly_out *out)
641{
642 return out->func_printed;
643}
644
Michal Vasko5233e962020-08-14 14:26:20 +0200645LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200646ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200647{
648 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200649 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200650 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100651 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
652 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200653 out->method.mem.len = 0;
654 out->method.mem.size = 0;
Michal Vasko5233e962020-08-14 14:26:20 +0200655 LOGMEM(NULL);
656 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200657 }
658 out->method.mem.size = out->method.mem.len + count;
659 }
660
661 /* save the current position */
662 *position = out->method.mem.len;
663
664 /* skip the memory */
665 out->method.mem.len += count;
666 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200667 case LY_OUT_FD:
668 case LY_OUT_FDSTREAM:
669 case LY_OUT_FILEPATH:
670 case LY_OUT_FILE:
671 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200672 /* buffer the hole */
673 if (out->buf_len + count > out->buf_size) {
674 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
675 if (!out->buffered) {
676 out->buf_len = 0;
677 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200678 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200679 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200680 }
681 out->buf_size = out->buf_len + count;
682 }
683
684 /* save the current position */
685 *position = out->buf_len;
686
687 /* skip the memory */
688 out->buf_len += count;
689
690 /* increase hole counter */
691 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100692 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200693 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100694 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200695 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200696 }
697
Michal Vasko5233e962020-08-14 14:26:20 +0200698 /* update printed bytes counter despite we actually printed just a hole */
699 out->printed += count;
700 out->func_printed += count;
701 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200702}
703
Michal Vasko66d99972020-06-29 13:37:42 +0200704LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200705ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200706{
Michal Vasko66d99972020-06-29 13:37:42 +0200707 LY_ERR ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200708
709 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200710 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200711 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100712 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200713 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200714 case LY_OUT_FD:
715 case LY_OUT_FDSTREAM:
716 case LY_OUT_FILEPATH:
717 case LY_OUT_FILE:
718 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200719 if (out->buf_len < position + count) {
Michal Vasko5233e962020-08-14 14:26:20 +0200720 LOGMEM(NULL);
721 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200722 }
723
724 /* write into the hole */
725 memcpy(&out->buffered[position], buf, count);
726
727 /* decrease hole counter */
728 --out->hole_count;
729
730 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200731 /* all holes filled, we can write the buffer,
Michal Vasko5233e962020-08-14 14:26:20 +0200732 * printed bytes counter is updated by ly_write_() */
733 ret = ly_write_(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200734 out->buf_len = 0;
735 }
736 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200737 case LY_OUT_ERROR:
Michal Vasko5233e962020-08-14 14:26:20 +0200738 LOGINT(NULL);
739 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200740 }
741
Radek Krejci241f6b52020-05-21 18:13:49 +0200742 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100743 /* move the original file descriptor to the end of the output file */
744 lseek(out->method.fdstream.fd, 0, SEEK_END);
745 }
Michal Vasko66d99972020-06-29 13:37:42 +0200746 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200747}