blob: 37beb69629ee659ad90218bbb176b4d29dd810c8 [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 */
16#include <sys/cdefs.h>
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"
Radek Krejci47fab892020-11-05 17:02:41 +010034#include "tree_data.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020035#include "tree_schema.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020036
Radek Krejci857189e2020-09-01 13:26:36 +020037ly_bool
Radek Krejci1deb5be2020-08-26 16:43:36 +020038ly_should_print(const struct lyd_node *node, uint32_t options)
Michal Vasko9b368d32020-02-14 13:53:31 +010039{
Michal Vaskob4fd37f2021-02-26 10:09:44 +010040 const struct lyd_node *elem;
Michal Vasko9b368d32020-02-14 13:53:31 +010041
Radek Krejci7931b192020-06-25 17:05:03 +020042 if (options & LYD_PRINT_WD_TRIM) {
Michal Vasko9b368d32020-02-14 13:53:31 +010043 /* do not print default nodes */
44 if (node->flags & LYD_DEFAULT) {
45 /* implicit default node/NP container with only default nodes */
46 return 0;
47 } else if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci19611252020-10-04 13:54:53 +020048 if (lyd_is_default(node)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010049 /* explicit default node */
50 return 0;
51 }
52 }
Michal Vasko0d5ea7e2021-02-26 10:10:15 +010053 } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
Michal Vasko5be03d62020-12-09 18:08:39 +010054 /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
Michal Vaskob4fd37f2021-02-26 10:09:44 +010055 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 +010056 /* print only if it contains status data in its subtree */
57 LYD_TREE_DFS_BEGIN(node, elem) {
58 if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
59 if (elem->schema->flags & LYS_CONFIG_R) {
60 return 1;
61 }
Michal Vaskodb4f9e42020-06-01 17:29:56 +020062 }
Michal Vasko5be03d62020-12-09 18:08:39 +010063 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010064 }
Michal Vasko9b368d32020-02-14 13:53:31 +010065 }
66 return 0;
Radek Krejci7931b192020-06-25 17:05:03 +020067 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYD_PRINT_KEEPEMPTYCONT)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010068 /* avoid empty default containers */
Michal Vasko56daf732020-08-10 10:57:18 +020069 LYD_TREE_DFS_BEGIN(node, elem) {
Michal Vasko9b368d32020-02-14 13:53:31 +010070 if (elem->schema->nodetype != LYS_CONTAINER) {
71 return 1;
72 }
73 assert(elem->flags & LYD_DEFAULT);
Michal Vasko56daf732020-08-10 10:57:18 +020074 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010075 }
76 return 0;
77 }
78
79 return 1;
80}
81
Radek Krejci241f6b52020-05-21 18:13:49 +020082API LY_OUT_TYPE
83ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +010084{
Radek Krejci241f6b52020-05-21 18:13:49 +020085 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +010086 return out->type;
87}
88
Radek Krejci84ce7b12020-06-11 17:28:25 +020089API LY_ERR
Michal Vaskoce2b8742020-08-24 13:20:25 +020090ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +010091{
Radek Krejci84ce7b12020-06-11 17:28:25 +020092 LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +010093
Radek Krejci84ce7b12020-06-11 17:28:25 +020094 *out = calloc(1, sizeof **out);
95 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +010096
Radek Krejci84ce7b12020-06-11 17:28:25 +020097 (*out)->type = LY_OUT_CALLBACK;
98 (*out)->method.clb.func = writeclb;
Michal Vaskoce2b8742020-08-24 13:20:25 +020099 (*out)->method.clb.arg = user_data;
Radek Krejcia5bba312020-01-09 15:41:20 +0100100
Radek Krejci84ce7b12020-06-11 17:28:25 +0200101 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100102}
103
Michal Vasko69730152020-10-09 16:30:07 +0200104API ly_write_clb
105ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
Radek Krejcia5bba312020-01-09 15:41:20 +0100106{
107 void *prev_clb;
108
Radek Krejci241f6b52020-05-21 18:13:49 +0200109 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100110
111 prev_clb = out->method.clb.func;
112
113 if (writeclb) {
114 out->method.clb.func = writeclb;
115 }
116
117 return prev_clb;
118}
119
120API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200121ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100122{
123 void *prev_arg;
124
Radek Krejci241f6b52020-05-21 18:13:49 +0200125 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100126
127 prev_arg = out->method.clb.arg;
128
129 if (arg) {
130 out->method.clb.arg = arg;
131 }
132
133 return prev_arg;
134}
135
Radek Krejci84ce7b12020-06-11 17:28:25 +0200136API LY_ERR
137ly_out_new_fd(int fd, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100138{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200139 LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100140
Radek Krejci84ce7b12020-06-11 17:28:25 +0200141 *out = calloc(1, sizeof **out);
142 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200143 (*out)->type = LY_OUT_FD;
144 (*out)->method.fd = fd;
Radek Krejcia5bba312020-01-09 15:41:20 +0100145
Radek Krejci84ce7b12020-06-11 17:28:25 +0200146 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100147}
148
149API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200150ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100151{
152 int prev_fd;
153
Radek Krejci241f6b52020-05-21 18:13:49 +0200154 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100155
Radek Krejci241f6b52020-05-21 18:13:49 +0200156 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100157 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200158 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100159 prev_fd = out->method.fd;
160 }
161
162 if (fd != -1) {
163 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200164 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100165 int streamfd;
166 FILE *stream;
167
168 streamfd = dup(fd);
169 if (streamfd < 0) {
170 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
171 return -1;
172 }
173 stream = fdopen(streamfd, "a");
174 if (!stream) {
175 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
176 close(streamfd);
177 return -1;
178 }
179 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
180 fclose(out->method.fdstream.f);
181 out->method.fdstream.f = stream;
182 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200183 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100184 out->method.fd = fd;
185 }
186 }
187
188 return prev_fd;
189}
190
Radek Krejci84ce7b12020-06-11 17:28:25 +0200191API LY_ERR
192ly_out_new_file(FILE *f, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100193{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200194 LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100195
Radek Krejci84ce7b12020-06-11 17:28:25 +0200196 *out = calloc(1, sizeof **out);
197 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100198
Radek Krejci84ce7b12020-06-11 17:28:25 +0200199 (*out)->type = LY_OUT_FILE;
200 (*out)->method.f = f;
Radek Krejcia5bba312020-01-09 15:41:20 +0100201
Radek Krejci84ce7b12020-06-11 17:28:25 +0200202 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100203}
204
205API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200206ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100207{
208 FILE *prev_f;
209
Radek Krejci241f6b52020-05-21 18:13:49 +0200210 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100211
212 prev_f = out->method.f;
213
214 if (f) {
215 out->method.f = f;
216 }
217
218 return prev_f;
219}
220
Radek Krejci84ce7b12020-06-11 17:28:25 +0200221API LY_ERR
222ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100223{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200224 LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100225
Radek Krejci84ce7b12020-06-11 17:28:25 +0200226 *out = calloc(1, sizeof **out);
227 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100228
Radek Krejci84ce7b12020-06-11 17:28:25 +0200229 (*out)->type = LY_OUT_MEMORY;
230 (*out)->method.mem.buf = strp;
Radek Krejcia5bba312020-01-09 15:41:20 +0100231 if (!size) {
232 /* buffer is supposed to be allocated */
233 *strp = NULL;
234 } else if (*strp) {
235 /* there is already buffer to use */
Radek Krejci84ce7b12020-06-11 17:28:25 +0200236 (*out)->method.mem.size = size;
Radek Krejcia5bba312020-01-09 15:41:20 +0100237 }
238
Radek Krejci84ce7b12020-06-11 17:28:25 +0200239 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100240}
241
242char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200243ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100244{
245 char *data;
246
Radek Krejci241f6b52020-05-21 18:13:49 +0200247 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100248
249 data = *out->method.mem.buf;
250
251 if (strp) {
252 out->method.mem.buf = strp;
253 out->method.mem.len = out->method.mem.size = 0;
254 out->printed = 0;
255 if (!size) {
256 /* buffer is supposed to be allocated */
257 *strp = NULL;
258 } else if (*strp) {
259 /* there is already buffer to use */
260 out->method.mem.size = size;
261 }
262 }
263
264 return data;
265}
266
267API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200268ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100269{
270 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
271
Michal Vaskod989ba02020-08-24 10:59:24 +0200272 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200273 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100274 LOGINT(NULL);
275 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200276 case LY_OUT_FD:
Michal Vasko69730152020-10-09 16:30:07 +0200277 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100278 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
279 return LY_ESYS;
280 }
Michal Vasko69730152020-10-09 16:30:07 +0200281 if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200282 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
283 return LY_ESYS;
284 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100285 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200286 case LY_OUT_FDSTREAM:
287 case LY_OUT_FILE:
288 case LY_OUT_FILEPATH:
Michal Vasko69730152020-10-09 16:30:07 +0200289 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100290 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
291 return LY_ESYS;
292 }
Michal Vasko69730152020-10-09 16:30:07 +0200293 if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200294 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
295 return LY_ESYS;
296 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100297 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200298 case LY_OUT_MEMORY:
Radek Krejcic5a12e12020-05-27 17:09:59 +0200299 if (out->method.mem.buf && *out->method.mem.buf) {
300 memset(*out->method.mem.buf, 0, out->method.mem.len);
301 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100302 out->printed = 0;
303 out->method.mem.len = 0;
304 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200305 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100306 /* nothing to do (not seekable) */
307 break;
308 }
309
310 return LY_SUCCESS;
311}
312
Radek Krejci84ce7b12020-06-11 17:28:25 +0200313API LY_ERR
314ly_out_new_filepath(const char *filepath, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100315{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200316 LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100317
Radek Krejci84ce7b12020-06-11 17:28:25 +0200318 *out = calloc(1, sizeof **out);
319 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100320
Radek Krejci84ce7b12020-06-11 17:28:25 +0200321 (*out)->type = LY_OUT_FILEPATH;
322 (*out)->method.fpath.f = fopen(filepath, "w");
323 if (!(*out)->method.fpath.f) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100324 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
Radek Krejcif6923e82020-07-02 16:36:53 +0200325 free(*out);
326 *out = NULL;
Radek Krejci84ce7b12020-06-11 17:28:25 +0200327 return LY_ESYS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100328 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200329 (*out)->method.fpath.filepath = strdup(filepath);
330 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100331}
332
333API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200334ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100335{
336 FILE *f;
337
Radek Krejci241f6b52020-05-21 18:13:49 +0200338 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100339
340 if (!filepath) {
341 return out->method.fpath.filepath;
342 }
343
344 /* replace filepath */
345 f = out->method.fpath.f;
346 out->method.fpath.f = fopen(filepath, "w");
347 if (!out->method.fpath.f) {
348 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
349 out->method.fpath.f = f;
Michal Vasko69730152020-10-09 16:30:07 +0200350 return (void *)-1;
Radek Krejcia5bba312020-01-09 15:41:20 +0100351 }
352 fclose(f);
353 free(out->method.fpath.filepath);
354 out->method.fpath.filepath = strdup(filepath);
355
356 return NULL;
357}
358
359API void
Radek Krejci857189e2020-09-01 13:26:36 +0200360ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100361{
362 if (!out) {
363 return;
364 }
365
366 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200367 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100368 if (clb_arg_destructor) {
369 clb_arg_destructor(out->method.clb.arg);
370 }
371 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200372 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100373 fclose(out->method.fdstream.f);
374 if (destroy) {
375 close(out->method.fdstream.fd);
376 }
377 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200378 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100379 if (destroy) {
380 close(out->method.fd);
381 }
382 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200383 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100384 if (destroy) {
385 fclose(out->method.f);
386 }
387 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200388 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100389 if (destroy) {
390 free(*out->method.mem.buf);
391 }
392 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200393 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100394 free(out->method.fpath.filepath);
Radek Krejci2aae3752020-05-27 18:16:30 +0200395 fclose(out->method.fpath.f);
Radek Krejcia5bba312020-01-09 15:41:20 +0100396 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200397 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100398 LOGINT(NULL);
399 }
Michal Vasko159b8872020-11-18 18:14:16 +0100400
401 free(out->buffered);
Radek Krejcia5bba312020-01-09 15:41:20 +0100402 free(out);
403}
404
Michal Vasko5233e962020-08-14 14:26:20 +0200405static LY_ERR
406ly_vprint_(struct ly_out *out, const char *format, va_list ap)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200407{
Michal Vasko5233e962020-08-14 14:26:20 +0200408 LY_ERR ret;
409 int written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200410 char *msg = NULL, *aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200411
412 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200413 case LY_OUT_FD:
Michal Vasko5233e962020-08-14 14:26:20 +0200414 written = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200415 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200416 case LY_OUT_FDSTREAM:
417 case LY_OUT_FILEPATH:
418 case LY_OUT_FILE:
Michal Vasko5233e962020-08-14 14:26:20 +0200419 written = vfprintf(out->method.f, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200420 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200421 case LY_OUT_MEMORY:
Michal Vasko5233e962020-08-14 14:26:20 +0200422 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200423 break;
424 }
Michal Vasko5233e962020-08-14 14:26:20 +0200425 if (out->method.mem.len + written + 1 > out->method.mem.size) {
426 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200427 if (!aux) {
428 out->method.mem.buf = NULL;
429 out->method.mem.len = 0;
430 out->method.mem.size = 0;
431 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200432 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200433 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100434 *out->method.mem.buf = aux;
Michal Vasko5233e962020-08-14 14:26:20 +0200435 out->method.mem.size = out->method.mem.len + written + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200436 }
Michal Vasko5233e962020-08-14 14:26:20 +0200437 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
438 out->method.mem.len += written;
Radek Krejcia5bba312020-01-09 15:41:20 +0100439 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200440 free(msg);
441 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200442 case LY_OUT_CALLBACK:
Michal Vasko5233e962020-08-14 14:26:20 +0200443 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200444 break;
445 }
Michal Vasko5233e962020-08-14 14:26:20 +0200446 written = out->method.clb.func(out->method.clb.arg, msg, written);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200447 free(msg);
448 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200449 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100450 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200451 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200452 }
453
Michal Vasko5233e962020-08-14 14:26:20 +0200454 if (written < 0) {
455 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
456 written = 0;
457 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200458 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200459 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100460 /* move the original file descriptor to the end of the output file */
461 lseek(out->method.fdstream.fd, 0, SEEK_END);
462 }
Michal Vasko5233e962020-08-14 14:26:20 +0200463 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200464 }
Michal Vasko5233e962020-08-14 14:26:20 +0200465
466 out->printed += written;
467 out->func_printed += written;
468 return ret;
469}
470
471LY_ERR
472ly_print_(struct ly_out *out, const char *format, ...)
473{
474 LY_ERR ret;
475 va_list ap;
476
477 va_start(ap, format);
478 ret = ly_vprint_(out, format, ap);
479 va_end(ap);
480
481 return ret;
482}
483
484API LY_ERR
485ly_print(struct ly_out *out, const char *format, ...)
486{
487 LY_ERR ret;
488 va_list ap;
489
490 out->func_printed = 0;
491
492 va_start(ap, format);
493 ret = ly_vprint_(out, format, ap);
494 va_end(ap);
495
496 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200497}
498
Radek Krejci2aae3752020-05-27 18:16:30 +0200499API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200500ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200501{
502 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200503 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100504 /* move the original file descriptor to the end of the output file */
505 lseek(out->method.fdstream.fd, 0, SEEK_END);
506 fflush(out->method.fdstream.f);
507 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200508 case LY_OUT_FILEPATH:
509 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200510 fflush(out->method.f);
511 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200512 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200513 fsync(out->method.fd);
514 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200515 case LY_OUT_MEMORY:
516 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200517 /* nothing to do */
518 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200519 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100520 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200521 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200522
523 free(out->buffered);
524 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200525}
526
Michal Vasko5233e962020-08-14 14:26:20 +0200527LY_ERR
528ly_write_(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200529{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200530 LY_ERR ret = LY_SUCCESS;
531 size_t written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200532
533 if (out->hole_count) {
534 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200535 if (out->buf_len + len > out->buf_size) {
536 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200537 if (!out->buffered) {
538 out->buf_len = 0;
539 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200540 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200541 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200542 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200543 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200544 }
545
Radek Krejcie7b95092019-05-15 11:03:07 +0200546 memcpy(&out->buffered[out->buf_len], buf, len);
547 out->buf_len += len;
Michal Vasko5233e962020-08-14 14:26:20 +0200548
549 out->printed += len;
550 out->func_printed += len;
551 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200552 }
553
Radek Krejci897ad2e2019-04-29 16:43:07 +0200554repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200555 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200556 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200557 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100558 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
559 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200560 out->method.mem.len = 0;
561 out->method.mem.size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200562 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200563 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200564 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200565 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200566 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100567 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
Radek Krejcie7b95092019-05-15 11:03:07 +0200568 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100569 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200570
Michal Vasko5233e962020-08-14 14:26:20 +0200571 written = len;
572 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200573 case LY_OUT_FD: {
574 ssize_t r;
575 r = write(out->method.fd, buf, len);
576 if (r < 0) {
577 ret = LY_ESYS;
578 } else {
579 written = (size_t)r;
580 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200581 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200582 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200583 case LY_OUT_FDSTREAM:
584 case LY_OUT_FILEPATH:
585 case LY_OUT_FILE:
Michal Vasko63f3d842020-07-08 10:10:14 +0200586 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200587 if (written != len) {
588 ret = LY_ESYS;
589 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200590 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200591 case LY_OUT_CALLBACK: {
592 ssize_t r;
593 r = out->method.clb.func(out->method.clb.arg, buf, len);
594 if (r < 0) {
595 ret = LY_ESYS;
596 } else {
597 written = (size_t)r;
598 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200599 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200600 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200601 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100602 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200603 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200604 }
605
Radek Krejci1deb5be2020-08-26 16:43:36 +0200606 if (ret) {
Michal Vasko69730152020-10-09 16:30:07 +0200607 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200608 ret = LY_SUCCESS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200609 goto repeat;
610 }
Michal Vasko5233e962020-08-14 14:26:20 +0200611 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
612 written = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200613 } else if ((size_t)written != len) {
Michal Vasko5233e962020-08-14 14:26:20 +0200614 LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
Michal Vasko69730152020-10-09 16:30:07 +0200615 len - (size_t)written, len);
Michal Vasko5233e962020-08-14 14:26:20 +0200616 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200617 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200618 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100619 /* move the original file descriptor to the end of the output file */
620 lseek(out->method.fdstream.fd, 0, SEEK_END);
621 }
Michal Vasko5233e962020-08-14 14:26:20 +0200622 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200623 }
Michal Vasko5233e962020-08-14 14:26:20 +0200624
625 out->printed += written;
626 out->func_printed += written;
627 return ret;
628}
629
630API LY_ERR
631ly_write(struct ly_out *out, const char *buf, size_t len)
632{
633 out->func_printed = 0;
634
635 return ly_write_(out, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200636}
637
Michal Vasko63f3d842020-07-08 10:10:14 +0200638API size_t
639ly_out_printed(const struct ly_out *out)
640{
641 return out->func_printed;
642}
643
Michal Vasko5233e962020-08-14 14:26:20 +0200644LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200645ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200646{
647 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200648 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200649 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100650 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
651 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200652 out->method.mem.len = 0;
653 out->method.mem.size = 0;
Michal Vasko5233e962020-08-14 14:26:20 +0200654 LOGMEM(NULL);
655 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200656 }
657 out->method.mem.size = out->method.mem.len + count;
658 }
659
660 /* save the current position */
661 *position = out->method.mem.len;
662
663 /* skip the memory */
664 out->method.mem.len += count;
665 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200666 case LY_OUT_FD:
667 case LY_OUT_FDSTREAM:
668 case LY_OUT_FILEPATH:
669 case LY_OUT_FILE:
670 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200671 /* buffer the hole */
672 if (out->buf_len + count > out->buf_size) {
673 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
674 if (!out->buffered) {
675 out->buf_len = 0;
676 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200677 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200678 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200679 }
680 out->buf_size = out->buf_len + count;
681 }
682
683 /* save the current position */
684 *position = out->buf_len;
685
686 /* skip the memory */
687 out->buf_len += count;
688
689 /* increase hole counter */
690 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100691 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200692 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100693 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200694 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200695 }
696
Michal Vasko5233e962020-08-14 14:26:20 +0200697 /* update printed bytes counter despite we actually printed just a hole */
698 out->printed += count;
699 out->func_printed += count;
700 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200701}
702
Michal Vasko66d99972020-06-29 13:37:42 +0200703LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200704ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200705{
Michal Vasko66d99972020-06-29 13:37:42 +0200706 LY_ERR ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200707
708 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200709 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200710 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100711 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200712 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200713 case LY_OUT_FD:
714 case LY_OUT_FDSTREAM:
715 case LY_OUT_FILEPATH:
716 case LY_OUT_FILE:
717 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200718 if (out->buf_len < position + count) {
Michal Vasko5233e962020-08-14 14:26:20 +0200719 LOGMEM(NULL);
720 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200721 }
722
723 /* write into the hole */
724 memcpy(&out->buffered[position], buf, count);
725
726 /* decrease hole counter */
727 --out->hole_count;
728
729 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200730 /* all holes filled, we can write the buffer,
Michal Vasko5233e962020-08-14 14:26:20 +0200731 * printed bytes counter is updated by ly_write_() */
732 ret = ly_write_(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200733 out->buf_len = 0;
734 }
735 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200736 case LY_OUT_ERROR:
Michal Vasko5233e962020-08-14 14:26:20 +0200737 LOGINT(NULL);
738 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200739 }
740
Radek Krejci241f6b52020-05-21 18:13:49 +0200741 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100742 /* move the original file descriptor to the end of the output file */
743 lseek(out->method.fdstream.fd, 0, SEEK_END);
744 }
Michal Vasko66d99972020-06-29 13:37:42 +0200745 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200746}