blob: 4590f2625821a20722ad058648fcaba95d753489 [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
Christian Hopps32874e12021-05-01 09:43:54 -040015#define _GNU_SOURCE /* asprintf, strdup */
Radek Krejcid3ca0632019-04-16 16:54:54 +020016
Michal Vaskoafac7822020-10-20 14:22:26 +020017#include "out.h"
18#include "out_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020019
20#include <assert.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020021#include <errno.h>
22#include <stdarg.h>
Radek Krejci47fab892020-11-05 17:02:41 +010023#include <stdint.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include <stdio.h>
25#include <stdlib.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020026#include <string.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020027#include <unistd.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020028
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "common.h"
Michal Vasko5aa44c02020-06-29 11:47:02 +020030#include "compat.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020031#include "log.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020032#include "printer_data.h"
Radek Krejci47fab892020-11-05 17:02:41 +010033#include "tree_data.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020034#include "tree_schema.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020035
Radek Krejci857189e2020-09-01 13:26:36 +020036ly_bool
Radek Krejci1deb5be2020-08-26 16:43:36 +020037ly_should_print(const struct lyd_node *node, uint32_t options)
Michal Vasko9b368d32020-02-14 13:53:31 +010038{
Michal Vaskob4fd37f2021-02-26 10:09:44 +010039 const struct lyd_node *elem;
Michal Vasko9b368d32020-02-14 13:53:31 +010040
Radek Krejci7931b192020-06-25 17:05:03 +020041 if (options & LYD_PRINT_WD_TRIM) {
Michal Vasko9b368d32020-02-14 13:53:31 +010042 /* do not print default nodes */
43 if (node->flags & LYD_DEFAULT) {
44 /* implicit default node/NP container with only default nodes */
45 return 0;
46 } else if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci19611252020-10-04 13:54:53 +020047 if (lyd_is_default(node)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010048 /* explicit default node */
49 return 0;
50 }
51 }
Michal Vasko0d5ea7e2021-02-26 10:10:15 +010052 } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
Michal Vasko5be03d62020-12-09 18:08:39 +010053 /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
Michal Vaskob4fd37f2021-02-26 10:09:44 +010054 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 +010055 /* print only if it contains status data in its subtree */
56 LYD_TREE_DFS_BEGIN(node, elem) {
57 if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
58 if (elem->schema->flags & LYS_CONFIG_R) {
59 return 1;
60 }
Michal Vaskodb4f9e42020-06-01 17:29:56 +020061 }
Michal Vasko5be03d62020-12-09 18:08:39 +010062 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010063 }
Michal Vasko9b368d32020-02-14 13:53:31 +010064 }
65 return 0;
Radek Krejci7931b192020-06-25 17:05:03 +020066 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYD_PRINT_KEEPEMPTYCONT)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010067 /* avoid empty default containers */
Michal Vasko56daf732020-08-10 10:57:18 +020068 LYD_TREE_DFS_BEGIN(node, elem) {
Michal Vasko9b368d32020-02-14 13:53:31 +010069 if (elem->schema->nodetype != LYS_CONTAINER) {
70 return 1;
71 }
72 assert(elem->flags & LYD_DEFAULT);
Michal Vasko56daf732020-08-10 10:57:18 +020073 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010074 }
75 return 0;
76 }
77
78 return 1;
79}
80
Radek Krejci241f6b52020-05-21 18:13:49 +020081API LY_OUT_TYPE
82ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +010083{
Radek Krejci241f6b52020-05-21 18:13:49 +020084 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +010085 return out->type;
86}
87
Radek Krejci84ce7b12020-06-11 17:28:25 +020088API LY_ERR
Michal Vaskoce2b8742020-08-24 13:20:25 +020089ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +010090{
Radek Krejci84ce7b12020-06-11 17:28:25 +020091 LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +010092
Radek Krejci84ce7b12020-06-11 17:28:25 +020093 *out = calloc(1, sizeof **out);
94 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +010095
Radek Krejci84ce7b12020-06-11 17:28:25 +020096 (*out)->type = LY_OUT_CALLBACK;
97 (*out)->method.clb.func = writeclb;
Michal Vaskoce2b8742020-08-24 13:20:25 +020098 (*out)->method.clb.arg = user_data;
Radek Krejcia5bba312020-01-09 15:41:20 +010099
Radek Krejci84ce7b12020-06-11 17:28:25 +0200100 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100101}
102
Michal Vasko69730152020-10-09 16:30:07 +0200103API ly_write_clb
104ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
Radek Krejcia5bba312020-01-09 15:41:20 +0100105{
106 void *prev_clb;
107
Radek Krejci241f6b52020-05-21 18:13:49 +0200108 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100109
110 prev_clb = out->method.clb.func;
111
112 if (writeclb) {
113 out->method.clb.func = writeclb;
114 }
115
116 return prev_clb;
117}
118
119API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200120ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100121{
122 void *prev_arg;
123
Radek Krejci241f6b52020-05-21 18:13:49 +0200124 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100125
126 prev_arg = out->method.clb.arg;
127
128 if (arg) {
129 out->method.clb.arg = arg;
130 }
131
132 return prev_arg;
133}
134
Radek Krejci84ce7b12020-06-11 17:28:25 +0200135API LY_ERR
136ly_out_new_fd(int fd, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100137{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200138 LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100139
Radek Krejci84ce7b12020-06-11 17:28:25 +0200140 *out = calloc(1, sizeof **out);
141 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200142 (*out)->type = LY_OUT_FD;
143 (*out)->method.fd = fd;
Radek Krejcia5bba312020-01-09 15:41:20 +0100144
Radek Krejci84ce7b12020-06-11 17:28:25 +0200145 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100146}
147
148API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200149ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100150{
151 int prev_fd;
152
Radek Krejci241f6b52020-05-21 18:13:49 +0200153 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100154
Radek Krejci241f6b52020-05-21 18:13:49 +0200155 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100156 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200157 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100158 prev_fd = out->method.fd;
159 }
160
161 if (fd != -1) {
162 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200163 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100164 int streamfd;
165 FILE *stream;
166
167 streamfd = dup(fd);
168 if (streamfd < 0) {
169 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
170 return -1;
171 }
172 stream = fdopen(streamfd, "a");
173 if (!stream) {
174 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
175 close(streamfd);
176 return -1;
177 }
178 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
179 fclose(out->method.fdstream.f);
180 out->method.fdstream.f = stream;
181 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200182 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100183 out->method.fd = fd;
184 }
185 }
186
187 return prev_fd;
188}
189
Radek Krejci84ce7b12020-06-11 17:28:25 +0200190API LY_ERR
191ly_out_new_file(FILE *f, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100192{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200193 LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100194
Radek Krejci84ce7b12020-06-11 17:28:25 +0200195 *out = calloc(1, sizeof **out);
196 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100197
Radek Krejci84ce7b12020-06-11 17:28:25 +0200198 (*out)->type = LY_OUT_FILE;
199 (*out)->method.f = f;
Radek Krejcia5bba312020-01-09 15:41:20 +0100200
Radek Krejci84ce7b12020-06-11 17:28:25 +0200201 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100202}
203
204API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200205ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100206{
207 FILE *prev_f;
208
Radek Krejci241f6b52020-05-21 18:13:49 +0200209 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100210
211 prev_f = out->method.f;
212
213 if (f) {
214 out->method.f = f;
215 }
216
217 return prev_f;
218}
219
Radek Krejci84ce7b12020-06-11 17:28:25 +0200220API LY_ERR
221ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100222{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200223 LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100224
Radek Krejci84ce7b12020-06-11 17:28:25 +0200225 *out = calloc(1, sizeof **out);
226 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100227
Radek Krejci84ce7b12020-06-11 17:28:25 +0200228 (*out)->type = LY_OUT_MEMORY;
229 (*out)->method.mem.buf = strp;
Radek Krejcia5bba312020-01-09 15:41:20 +0100230 if (!size) {
231 /* buffer is supposed to be allocated */
232 *strp = NULL;
233 } else if (*strp) {
234 /* there is already buffer to use */
Radek Krejci84ce7b12020-06-11 17:28:25 +0200235 (*out)->method.mem.size = size;
Radek Krejcia5bba312020-01-09 15:41:20 +0100236 }
237
Radek Krejci84ce7b12020-06-11 17:28:25 +0200238 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100239}
240
241char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200242ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100243{
244 char *data;
245
Radek Krejci241f6b52020-05-21 18:13:49 +0200246 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100247
248 data = *out->method.mem.buf;
249
250 if (strp) {
251 out->method.mem.buf = strp;
252 out->method.mem.len = out->method.mem.size = 0;
253 out->printed = 0;
254 if (!size) {
255 /* buffer is supposed to be allocated */
256 *strp = NULL;
257 } else if (*strp) {
258 /* there is already buffer to use */
259 out->method.mem.size = size;
260 }
261 }
262
263 return data;
264}
265
266API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200267ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100268{
269 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
270
Michal Vaskod989ba02020-08-24 10:59:24 +0200271 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200272 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100273 LOGINT(NULL);
274 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200275 case LY_OUT_FD:
Michal Vasko69730152020-10-09 16:30:07 +0200276 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100277 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
278 return LY_ESYS;
279 }
Michal Vasko69730152020-10-09 16:30:07 +0200280 if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200281 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
282 return LY_ESYS;
283 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100284 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200285 case LY_OUT_FDSTREAM:
286 case LY_OUT_FILE:
287 case LY_OUT_FILEPATH:
Michal Vasko69730152020-10-09 16:30:07 +0200288 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100289 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
290 return LY_ESYS;
291 }
Michal Vasko69730152020-10-09 16:30:07 +0200292 if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200293 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
294 return LY_ESYS;
295 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100296 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200297 case LY_OUT_MEMORY:
Radek Krejcic5a12e12020-05-27 17:09:59 +0200298 if (out->method.mem.buf && *out->method.mem.buf) {
299 memset(*out->method.mem.buf, 0, out->method.mem.len);
300 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100301 out->printed = 0;
302 out->method.mem.len = 0;
303 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200304 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100305 /* nothing to do (not seekable) */
306 break;
307 }
308
309 return LY_SUCCESS;
310}
311
Radek Krejci84ce7b12020-06-11 17:28:25 +0200312API LY_ERR
313ly_out_new_filepath(const char *filepath, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100314{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200315 LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100316
Radek Krejci84ce7b12020-06-11 17:28:25 +0200317 *out = calloc(1, sizeof **out);
318 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100319
Radek Krejci84ce7b12020-06-11 17:28:25 +0200320 (*out)->type = LY_OUT_FILEPATH;
321 (*out)->method.fpath.f = fopen(filepath, "w");
322 if (!(*out)->method.fpath.f) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100323 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
Radek Krejcif6923e82020-07-02 16:36:53 +0200324 free(*out);
325 *out = NULL;
Radek Krejci84ce7b12020-06-11 17:28:25 +0200326 return LY_ESYS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100327 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200328 (*out)->method.fpath.filepath = strdup(filepath);
329 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100330}
331
332API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200333ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100334{
335 FILE *f;
336
Radek Krejci241f6b52020-05-21 18:13:49 +0200337 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100338
339 if (!filepath) {
340 return out->method.fpath.filepath;
341 }
342
343 /* replace filepath */
344 f = out->method.fpath.f;
345 out->method.fpath.f = fopen(filepath, "w");
346 if (!out->method.fpath.f) {
347 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
348 out->method.fpath.f = f;
Michal Vasko69730152020-10-09 16:30:07 +0200349 return (void *)-1;
Radek Krejcia5bba312020-01-09 15:41:20 +0100350 }
351 fclose(f);
352 free(out->method.fpath.filepath);
353 out->method.fpath.filepath = strdup(filepath);
354
355 return NULL;
356}
357
358API void
Radek Krejci857189e2020-09-01 13:26:36 +0200359ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100360{
361 if (!out) {
362 return;
363 }
364
365 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200366 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100367 if (clb_arg_destructor) {
368 clb_arg_destructor(out->method.clb.arg);
369 }
370 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200371 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100372 fclose(out->method.fdstream.f);
373 if (destroy) {
374 close(out->method.fdstream.fd);
375 }
376 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200377 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100378 if (destroy) {
379 close(out->method.fd);
380 }
381 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200382 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100383 if (destroy) {
384 fclose(out->method.f);
385 }
386 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200387 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100388 if (destroy) {
389 free(*out->method.mem.buf);
390 }
391 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200392 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100393 free(out->method.fpath.filepath);
Radek Krejci2aae3752020-05-27 18:16:30 +0200394 fclose(out->method.fpath.f);
Radek Krejcia5bba312020-01-09 15:41:20 +0100395 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200396 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100397 LOGINT(NULL);
398 }
Michal Vasko159b8872020-11-18 18:14:16 +0100399
400 free(out->buffered);
Radek Krejcia5bba312020-01-09 15:41:20 +0100401 free(out);
402}
403
Michal Vasko5233e962020-08-14 14:26:20 +0200404static LY_ERR
405ly_vprint_(struct ly_out *out, const char *format, va_list ap)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200406{
Michal Vasko5233e962020-08-14 14:26:20 +0200407 LY_ERR ret;
408 int written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200409 char *msg = NULL, *aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200410
411 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200412 case LY_OUT_FD:
Michal Vasko5233e962020-08-14 14:26:20 +0200413 written = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200414 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200415 case LY_OUT_FDSTREAM:
416 case LY_OUT_FILEPATH:
417 case LY_OUT_FILE:
Michal Vasko5233e962020-08-14 14:26:20 +0200418 written = vfprintf(out->method.f, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200419 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200420 case LY_OUT_MEMORY:
Michal Vasko5233e962020-08-14 14:26:20 +0200421 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200422 break;
423 }
Michal Vasko5233e962020-08-14 14:26:20 +0200424 if (out->method.mem.len + written + 1 > out->method.mem.size) {
425 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200426 if (!aux) {
427 out->method.mem.buf = NULL;
428 out->method.mem.len = 0;
429 out->method.mem.size = 0;
430 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200431 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200432 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100433 *out->method.mem.buf = aux;
Michal Vasko5233e962020-08-14 14:26:20 +0200434 out->method.mem.size = out->method.mem.len + written + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200435 }
Michal Vasko08e9b112021-06-11 15:41:17 +0200436 if (written) {
437 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
438 }
Michal Vasko5233e962020-08-14 14:26:20 +0200439 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
Michal Vasko08e9b112021-06-11 15:41:17 +0200547 if (len) {
548 memcpy(&out->buffered[out->buf_len], buf, len);
549 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200550 out->buf_len += len;
Michal Vasko5233e962020-08-14 14:26:20 +0200551
552 out->printed += len;
553 out->func_printed += len;
554 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200555 }
556
Radek Krejci897ad2e2019-04-29 16:43:07 +0200557repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200558 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200559 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200560 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100561 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
562 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200563 out->method.mem.len = 0;
564 out->method.mem.size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200565 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200566 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200567 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200568 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200569 }
Michal Vasko08e9b112021-06-11 15:41:17 +0200570 if (len) {
571 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
572 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200573 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100574 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200575
Michal Vasko5233e962020-08-14 14:26:20 +0200576 written = len;
577 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200578 case LY_OUT_FD: {
579 ssize_t r;
580 r = write(out->method.fd, buf, len);
581 if (r < 0) {
582 ret = LY_ESYS;
583 } else {
584 written = (size_t)r;
585 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200586 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200587 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200588 case LY_OUT_FDSTREAM:
589 case LY_OUT_FILEPATH:
590 case LY_OUT_FILE:
Michal Vasko63f3d842020-07-08 10:10:14 +0200591 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200592 if (written != len) {
593 ret = LY_ESYS;
594 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200595 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200596 case LY_OUT_CALLBACK: {
597 ssize_t r;
598 r = out->method.clb.func(out->method.clb.arg, buf, len);
599 if (r < 0) {
600 ret = LY_ESYS;
601 } else {
602 written = (size_t)r;
603 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200604 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200605 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200606 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100607 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200608 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200609 }
610
Radek Krejci1deb5be2020-08-26 16:43:36 +0200611 if (ret) {
Michal Vasko69730152020-10-09 16:30:07 +0200612 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200613 ret = LY_SUCCESS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200614 goto repeat;
615 }
Michal Vasko5233e962020-08-14 14:26:20 +0200616 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
617 written = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200618 } else if ((size_t)written != len) {
Michal Vasko5233e962020-08-14 14:26:20 +0200619 LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
Michal Vasko69730152020-10-09 16:30:07 +0200620 len - (size_t)written, len);
Michal Vasko5233e962020-08-14 14:26:20 +0200621 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200622 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200623 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100624 /* move the original file descriptor to the end of the output file */
625 lseek(out->method.fdstream.fd, 0, SEEK_END);
626 }
Michal Vasko5233e962020-08-14 14:26:20 +0200627 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200628 }
Michal Vasko5233e962020-08-14 14:26:20 +0200629
630 out->printed += written;
631 out->func_printed += written;
632 return ret;
633}
634
635API LY_ERR
636ly_write(struct ly_out *out, const char *buf, size_t len)
637{
638 out->func_printed = 0;
639
640 return ly_write_(out, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200641}
642
Michal Vasko63f3d842020-07-08 10:10:14 +0200643API size_t
644ly_out_printed(const struct ly_out *out)
645{
646 return out->func_printed;
647}
648
Michal Vasko5233e962020-08-14 14:26:20 +0200649LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200650ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200651{
652 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200653 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200654 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100655 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
656 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200657 out->method.mem.len = 0;
658 out->method.mem.size = 0;
Michal Vasko5233e962020-08-14 14:26:20 +0200659 LOGMEM(NULL);
660 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200661 }
662 out->method.mem.size = out->method.mem.len + count;
663 }
664
665 /* save the current position */
666 *position = out->method.mem.len;
667
668 /* skip the memory */
669 out->method.mem.len += count;
670 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200671 case LY_OUT_FD:
672 case LY_OUT_FDSTREAM:
673 case LY_OUT_FILEPATH:
674 case LY_OUT_FILE:
675 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200676 /* buffer the hole */
677 if (out->buf_len + count > out->buf_size) {
678 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
679 if (!out->buffered) {
680 out->buf_len = 0;
681 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200682 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200683 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200684 }
685 out->buf_size = out->buf_len + count;
686 }
687
688 /* save the current position */
689 *position = out->buf_len;
690
691 /* skip the memory */
692 out->buf_len += count;
693
694 /* increase hole counter */
695 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100696 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200697 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100698 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200699 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200700 }
701
Michal Vasko5233e962020-08-14 14:26:20 +0200702 /* update printed bytes counter despite we actually printed just a hole */
703 out->printed += count;
704 out->func_printed += count;
705 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200706}
707
Michal Vasko66d99972020-06-29 13:37:42 +0200708LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200709ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200710{
Michal Vasko66d99972020-06-29 13:37:42 +0200711 LY_ERR ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200712
Michal Vasko08e9b112021-06-11 15:41:17 +0200713 assert(count);
714
Radek Krejcid3ca0632019-04-16 16:54:54 +0200715 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200716 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200717 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100718 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200719 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200720 case LY_OUT_FD:
721 case LY_OUT_FDSTREAM:
722 case LY_OUT_FILEPATH:
723 case LY_OUT_FILE:
724 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200725 if (out->buf_len < position + count) {
Michal Vasko5233e962020-08-14 14:26:20 +0200726 LOGMEM(NULL);
727 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200728 }
729
730 /* write into the hole */
731 memcpy(&out->buffered[position], buf, count);
732
733 /* decrease hole counter */
734 --out->hole_count;
735
736 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200737 /* all holes filled, we can write the buffer,
Michal Vasko5233e962020-08-14 14:26:20 +0200738 * printed bytes counter is updated by ly_write_() */
739 ret = ly_write_(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200740 out->buf_len = 0;
741 }
742 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200743 case LY_OUT_ERROR:
Michal Vasko5233e962020-08-14 14:26:20 +0200744 LOGINT(NULL);
745 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200746 }
747
Radek Krejci241f6b52020-05-21 18:13:49 +0200748 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100749 /* move the original file descriptor to the end of the output file */
750 lseek(out->method.fdstream.fd, 0, SEEK_END);
751 }
Michal Vasko66d99972020-06-29 13:37:42 +0200752 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200753}