blob: d848b25b94421f884b60bee59423ef1bf1c8b469 [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;
Michal Vaskobd777662021-07-09 13:58:40 +020046 } else if (node->schema && (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 Vasko1dd384e2021-07-08 13:59:55 +020052 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER)) {
53 if (options & LYD_PRINT_KEEPEMPTYCONT) {
54 /* explicit request to print */
55 return 1;
56 }
57
58 /* avoid empty default containers */
59 LYD_TREE_DFS_BEGIN(node, elem) {
60 if ((elem != node) && ly_should_print(elem, options)) {
61 return 1;
62 }
63 assert(elem->flags & LYD_DEFAULT);
64 LYD_TREE_DFS_END(node, elem)
65 }
66 return 0;
Michal Vasko0d5ea7e2021-02-26 10:10:15 +010067 } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
Michal Vasko5be03d62020-12-09 18:08:39 +010068 /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
Michal Vaskob4fd37f2021-02-26 10:09:44 +010069 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 +010070 /* print only if it contains status data in its subtree */
71 LYD_TREE_DFS_BEGIN(node, elem) {
72 if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
73 if (elem->schema->flags & LYS_CONFIG_R) {
74 return 1;
75 }
Michal Vaskodb4f9e42020-06-01 17:29:56 +020076 }
Michal Vasko5be03d62020-12-09 18:08:39 +010077 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010078 }
Michal Vasko9b368d32020-02-14 13:53:31 +010079 }
80 return 0;
Michal Vasko9b368d32020-02-14 13:53:31 +010081 }
82
83 return 1;
84}
85
Radek Krejci241f6b52020-05-21 18:13:49 +020086API LY_OUT_TYPE
87ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +010088{
Radek Krejci241f6b52020-05-21 18:13:49 +020089 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +010090 return out->type;
91}
92
Radek Krejci84ce7b12020-06-11 17:28:25 +020093API LY_ERR
Michal Vaskoce2b8742020-08-24 13:20:25 +020094ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +010095{
Radek Krejci84ce7b12020-06-11 17:28:25 +020096 LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +010097
Radek Krejci84ce7b12020-06-11 17:28:25 +020098 *out = calloc(1, sizeof **out);
99 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100100
Radek Krejci84ce7b12020-06-11 17:28:25 +0200101 (*out)->type = LY_OUT_CALLBACK;
102 (*out)->method.clb.func = writeclb;
Michal Vaskoce2b8742020-08-24 13:20:25 +0200103 (*out)->method.clb.arg = user_data;
Radek Krejcia5bba312020-01-09 15:41:20 +0100104
Radek Krejci84ce7b12020-06-11 17:28:25 +0200105 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100106}
107
Michal Vasko69730152020-10-09 16:30:07 +0200108API ly_write_clb
109ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
Radek Krejcia5bba312020-01-09 15:41:20 +0100110{
111 void *prev_clb;
112
Radek Krejci241f6b52020-05-21 18:13:49 +0200113 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100114
115 prev_clb = out->method.clb.func;
116
117 if (writeclb) {
118 out->method.clb.func = writeclb;
119 }
120
121 return prev_clb;
122}
123
124API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200125ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100126{
127 void *prev_arg;
128
Radek Krejci241f6b52020-05-21 18:13:49 +0200129 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100130
131 prev_arg = out->method.clb.arg;
132
133 if (arg) {
134 out->method.clb.arg = arg;
135 }
136
137 return prev_arg;
138}
139
Radek Krejci84ce7b12020-06-11 17:28:25 +0200140API LY_ERR
141ly_out_new_fd(int fd, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100142{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200143 LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100144
Radek Krejci84ce7b12020-06-11 17:28:25 +0200145 *out = calloc(1, sizeof **out);
146 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200147 (*out)->type = LY_OUT_FD;
148 (*out)->method.fd = fd;
Radek Krejcia5bba312020-01-09 15:41:20 +0100149
Radek Krejci84ce7b12020-06-11 17:28:25 +0200150 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100151}
152
153API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200154ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100155{
156 int prev_fd;
157
Radek Krejci241f6b52020-05-21 18:13:49 +0200158 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100159
Radek Krejci241f6b52020-05-21 18:13:49 +0200160 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100161 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200162 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100163 prev_fd = out->method.fd;
164 }
165
166 if (fd != -1) {
167 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200168 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100169 int streamfd;
170 FILE *stream;
171
172 streamfd = dup(fd);
173 if (streamfd < 0) {
174 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
175 return -1;
176 }
177 stream = fdopen(streamfd, "a");
178 if (!stream) {
179 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
180 close(streamfd);
181 return -1;
182 }
183 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
184 fclose(out->method.fdstream.f);
185 out->method.fdstream.f = stream;
186 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200187 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100188 out->method.fd = fd;
189 }
190 }
191
192 return prev_fd;
193}
194
Radek Krejci84ce7b12020-06-11 17:28:25 +0200195API LY_ERR
196ly_out_new_file(FILE *f, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100197{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200198 LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100199
Radek Krejci84ce7b12020-06-11 17:28:25 +0200200 *out = calloc(1, sizeof **out);
201 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100202
Radek Krejci84ce7b12020-06-11 17:28:25 +0200203 (*out)->type = LY_OUT_FILE;
204 (*out)->method.f = f;
Radek Krejcia5bba312020-01-09 15:41:20 +0100205
Radek Krejci84ce7b12020-06-11 17:28:25 +0200206 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100207}
208
209API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200210ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100211{
212 FILE *prev_f;
213
Radek Krejci241f6b52020-05-21 18:13:49 +0200214 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100215
216 prev_f = out->method.f;
217
218 if (f) {
219 out->method.f = f;
220 }
221
222 return prev_f;
223}
224
Radek Krejci84ce7b12020-06-11 17:28:25 +0200225API LY_ERR
226ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100227{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200228 LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100229
Radek Krejci84ce7b12020-06-11 17:28:25 +0200230 *out = calloc(1, sizeof **out);
231 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100232
Radek Krejci84ce7b12020-06-11 17:28:25 +0200233 (*out)->type = LY_OUT_MEMORY;
234 (*out)->method.mem.buf = strp;
Radek Krejcia5bba312020-01-09 15:41:20 +0100235 if (!size) {
236 /* buffer is supposed to be allocated */
237 *strp = NULL;
238 } else if (*strp) {
239 /* there is already buffer to use */
Radek Krejci84ce7b12020-06-11 17:28:25 +0200240 (*out)->method.mem.size = size;
Radek Krejcia5bba312020-01-09 15:41:20 +0100241 }
242
Radek Krejci84ce7b12020-06-11 17:28:25 +0200243 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100244}
245
246char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200247ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100248{
249 char *data;
250
Radek Krejci241f6b52020-05-21 18:13:49 +0200251 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100252
253 data = *out->method.mem.buf;
254
255 if (strp) {
256 out->method.mem.buf = strp;
257 out->method.mem.len = out->method.mem.size = 0;
258 out->printed = 0;
259 if (!size) {
260 /* buffer is supposed to be allocated */
261 *strp = NULL;
262 } else if (*strp) {
263 /* there is already buffer to use */
264 out->method.mem.size = size;
265 }
266 }
267
268 return data;
269}
270
271API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200272ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100273{
274 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
275
Michal Vaskod989ba02020-08-24 10:59:24 +0200276 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200277 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100278 LOGINT(NULL);
279 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200280 case LY_OUT_FD:
Michal Vasko69730152020-10-09 16:30:07 +0200281 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100282 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
283 return LY_ESYS;
284 }
Michal Vasko69730152020-10-09 16:30:07 +0200285 if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200286 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
287 return LY_ESYS;
288 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100289 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200290 case LY_OUT_FDSTREAM:
291 case LY_OUT_FILE:
292 case LY_OUT_FILEPATH:
Michal Vasko69730152020-10-09 16:30:07 +0200293 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100294 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
295 return LY_ESYS;
296 }
Michal Vasko69730152020-10-09 16:30:07 +0200297 if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200298 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
299 return LY_ESYS;
300 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100301 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200302 case LY_OUT_MEMORY:
Radek Krejcic5a12e12020-05-27 17:09:59 +0200303 if (out->method.mem.buf && *out->method.mem.buf) {
304 memset(*out->method.mem.buf, 0, out->method.mem.len);
305 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100306 out->printed = 0;
307 out->method.mem.len = 0;
308 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200309 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100310 /* nothing to do (not seekable) */
311 break;
312 }
313
314 return LY_SUCCESS;
315}
316
Radek Krejci84ce7b12020-06-11 17:28:25 +0200317API LY_ERR
318ly_out_new_filepath(const char *filepath, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100319{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200320 LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100321
Radek Krejci84ce7b12020-06-11 17:28:25 +0200322 *out = calloc(1, sizeof **out);
323 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100324
Radek Krejci84ce7b12020-06-11 17:28:25 +0200325 (*out)->type = LY_OUT_FILEPATH;
326 (*out)->method.fpath.f = fopen(filepath, "w");
327 if (!(*out)->method.fpath.f) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100328 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
Radek Krejcif6923e82020-07-02 16:36:53 +0200329 free(*out);
330 *out = NULL;
Radek Krejci84ce7b12020-06-11 17:28:25 +0200331 return LY_ESYS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100332 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200333 (*out)->method.fpath.filepath = strdup(filepath);
334 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100335}
336
337API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200338ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100339{
340 FILE *f;
341
Radek Krejci241f6b52020-05-21 18:13:49 +0200342 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100343
344 if (!filepath) {
345 return out->method.fpath.filepath;
346 }
347
348 /* replace filepath */
349 f = out->method.fpath.f;
350 out->method.fpath.f = fopen(filepath, "w");
351 if (!out->method.fpath.f) {
352 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
353 out->method.fpath.f = f;
Michal Vasko69730152020-10-09 16:30:07 +0200354 return (void *)-1;
Radek Krejcia5bba312020-01-09 15:41:20 +0100355 }
356 fclose(f);
357 free(out->method.fpath.filepath);
358 out->method.fpath.filepath = strdup(filepath);
359
360 return NULL;
361}
362
363API void
Radek Krejci857189e2020-09-01 13:26:36 +0200364ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100365{
366 if (!out) {
367 return;
368 }
369
370 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200371 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100372 if (clb_arg_destructor) {
373 clb_arg_destructor(out->method.clb.arg);
374 }
375 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200376 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100377 fclose(out->method.fdstream.f);
378 if (destroy) {
379 close(out->method.fdstream.fd);
380 }
381 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200382 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100383 if (destroy) {
384 close(out->method.fd);
385 }
386 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200387 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100388 if (destroy) {
389 fclose(out->method.f);
390 }
391 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200392 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100393 if (destroy) {
394 free(*out->method.mem.buf);
395 }
396 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200397 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100398 free(out->method.fpath.filepath);
Radek Krejci2aae3752020-05-27 18:16:30 +0200399 fclose(out->method.fpath.f);
Radek Krejcia5bba312020-01-09 15:41:20 +0100400 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200401 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100402 LOGINT(NULL);
403 }
Michal Vasko159b8872020-11-18 18:14:16 +0100404
405 free(out->buffered);
Radek Krejcia5bba312020-01-09 15:41:20 +0100406 free(out);
407}
408
Michal Vasko5233e962020-08-14 14:26:20 +0200409static LY_ERR
410ly_vprint_(struct ly_out *out, const char *format, va_list ap)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200411{
Michal Vasko5233e962020-08-14 14:26:20 +0200412 LY_ERR ret;
413 int written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200414 char *msg = NULL, *aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200415
416 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200417 case LY_OUT_FD:
Michal Vasko5233e962020-08-14 14:26:20 +0200418 written = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200419 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200420 case LY_OUT_FDSTREAM:
421 case LY_OUT_FILEPATH:
422 case LY_OUT_FILE:
Michal Vasko5233e962020-08-14 14:26:20 +0200423 written = vfprintf(out->method.f, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200424 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200425 case LY_OUT_MEMORY:
Michal Vasko5233e962020-08-14 14:26:20 +0200426 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200427 break;
428 }
Michal Vasko5233e962020-08-14 14:26:20 +0200429 if (out->method.mem.len + written + 1 > out->method.mem.size) {
430 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200431 if (!aux) {
432 out->method.mem.buf = NULL;
433 out->method.mem.len = 0;
434 out->method.mem.size = 0;
435 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200436 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200437 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100438 *out->method.mem.buf = aux;
Michal Vasko5233e962020-08-14 14:26:20 +0200439 out->method.mem.size = out->method.mem.len + written + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200440 }
Michal Vasko08e9b112021-06-11 15:41:17 +0200441 if (written) {
442 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
443 }
Michal Vasko5233e962020-08-14 14:26:20 +0200444 out->method.mem.len += written;
Radek Krejcia5bba312020-01-09 15:41:20 +0100445 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200446 free(msg);
447 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200448 case LY_OUT_CALLBACK:
Michal Vasko5233e962020-08-14 14:26:20 +0200449 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200450 break;
451 }
Michal Vasko5233e962020-08-14 14:26:20 +0200452 written = out->method.clb.func(out->method.clb.arg, msg, written);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200453 free(msg);
454 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200455 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100456 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200457 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200458 }
459
Michal Vasko5233e962020-08-14 14:26:20 +0200460 if (written < 0) {
461 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
462 written = 0;
463 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200464 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200465 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100466 /* move the original file descriptor to the end of the output file */
467 lseek(out->method.fdstream.fd, 0, SEEK_END);
468 }
Michal Vasko5233e962020-08-14 14:26:20 +0200469 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200470 }
Michal Vasko5233e962020-08-14 14:26:20 +0200471
472 out->printed += written;
473 out->func_printed += written;
474 return ret;
475}
476
477LY_ERR
478ly_print_(struct ly_out *out, const char *format, ...)
479{
480 LY_ERR ret;
481 va_list ap;
482
483 va_start(ap, format);
484 ret = ly_vprint_(out, format, ap);
485 va_end(ap);
486
487 return ret;
488}
489
490API LY_ERR
491ly_print(struct ly_out *out, const char *format, ...)
492{
493 LY_ERR ret;
494 va_list ap;
495
496 out->func_printed = 0;
497
498 va_start(ap, format);
499 ret = ly_vprint_(out, format, ap);
500 va_end(ap);
501
502 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200503}
504
Radek Krejci2aae3752020-05-27 18:16:30 +0200505API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200506ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200507{
508 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200509 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100510 /* move the original file descriptor to the end of the output file */
511 lseek(out->method.fdstream.fd, 0, SEEK_END);
512 fflush(out->method.fdstream.f);
513 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200514 case LY_OUT_FILEPATH:
515 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200516 fflush(out->method.f);
517 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200518 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200519 fsync(out->method.fd);
520 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200521 case LY_OUT_MEMORY:
522 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200523 /* nothing to do */
524 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200525 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100526 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200527 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200528
529 free(out->buffered);
530 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200531}
532
Michal Vasko5233e962020-08-14 14:26:20 +0200533LY_ERR
534ly_write_(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200535{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200536 LY_ERR ret = LY_SUCCESS;
537 size_t written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200538
539 if (out->hole_count) {
540 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200541 if (out->buf_len + len > out->buf_size) {
542 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200543 if (!out->buffered) {
544 out->buf_len = 0;
545 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200546 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200547 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200548 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200549 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200550 }
551
Michal Vasko08e9b112021-06-11 15:41:17 +0200552 if (len) {
553 memcpy(&out->buffered[out->buf_len], buf, len);
554 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200555 out->buf_len += len;
Michal Vasko5233e962020-08-14 14:26:20 +0200556
557 out->printed += len;
558 out->func_printed += len;
559 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200560 }
561
Radek Krejci897ad2e2019-04-29 16:43:07 +0200562repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200563 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200564 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200565 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100566 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
567 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200568 out->method.mem.len = 0;
569 out->method.mem.size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200570 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200571 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200572 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200573 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200574 }
Michal Vasko08e9b112021-06-11 15:41:17 +0200575 if (len) {
576 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
577 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200578 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100579 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200580
Michal Vasko5233e962020-08-14 14:26:20 +0200581 written = len;
582 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200583 case LY_OUT_FD: {
584 ssize_t r;
585 r = write(out->method.fd, buf, len);
586 if (r < 0) {
587 ret = LY_ESYS;
588 } else {
589 written = (size_t)r;
590 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200591 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200592 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200593 case LY_OUT_FDSTREAM:
594 case LY_OUT_FILEPATH:
595 case LY_OUT_FILE:
Michal Vasko63f3d842020-07-08 10:10:14 +0200596 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200597 if (written != len) {
598 ret = LY_ESYS;
599 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200600 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200601 case LY_OUT_CALLBACK: {
602 ssize_t r;
603 r = out->method.clb.func(out->method.clb.arg, buf, len);
604 if (r < 0) {
605 ret = LY_ESYS;
606 } else {
607 written = (size_t)r;
608 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200609 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200610 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200611 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100612 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200613 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200614 }
615
Radek Krejci1deb5be2020-08-26 16:43:36 +0200616 if (ret) {
Michal Vasko69730152020-10-09 16:30:07 +0200617 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200618 ret = LY_SUCCESS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200619 goto repeat;
620 }
Michal Vasko5233e962020-08-14 14:26:20 +0200621 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
622 written = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200623 } else if ((size_t)written != len) {
Michal Vasko5233e962020-08-14 14:26:20 +0200624 LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
Michal Vasko69730152020-10-09 16:30:07 +0200625 len - (size_t)written, len);
Michal Vasko5233e962020-08-14 14:26:20 +0200626 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200627 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200628 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100629 /* move the original file descriptor to the end of the output file */
630 lseek(out->method.fdstream.fd, 0, SEEK_END);
631 }
Michal Vasko5233e962020-08-14 14:26:20 +0200632 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200633 }
Michal Vasko5233e962020-08-14 14:26:20 +0200634
635 out->printed += written;
636 out->func_printed += written;
637 return ret;
638}
639
640API LY_ERR
641ly_write(struct ly_out *out, const char *buf, size_t len)
642{
643 out->func_printed = 0;
644
645 return ly_write_(out, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200646}
647
Michal Vasko63f3d842020-07-08 10:10:14 +0200648API size_t
649ly_out_printed(const struct ly_out *out)
650{
651 return out->func_printed;
652}
653
Michal Vasko5233e962020-08-14 14:26:20 +0200654LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200655ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200656{
657 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200658 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200659 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100660 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
661 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200662 out->method.mem.len = 0;
663 out->method.mem.size = 0;
Michal Vasko5233e962020-08-14 14:26:20 +0200664 LOGMEM(NULL);
665 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200666 }
667 out->method.mem.size = out->method.mem.len + count;
668 }
669
670 /* save the current position */
671 *position = out->method.mem.len;
672
673 /* skip the memory */
674 out->method.mem.len += count;
675 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200676 case LY_OUT_FD:
677 case LY_OUT_FDSTREAM:
678 case LY_OUT_FILEPATH:
679 case LY_OUT_FILE:
680 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200681 /* buffer the hole */
682 if (out->buf_len + count > out->buf_size) {
683 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
684 if (!out->buffered) {
685 out->buf_len = 0;
686 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200687 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200688 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200689 }
690 out->buf_size = out->buf_len + count;
691 }
692
693 /* save the current position */
694 *position = out->buf_len;
695
696 /* skip the memory */
697 out->buf_len += count;
698
699 /* increase hole counter */
700 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100701 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200702 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100703 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200704 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200705 }
706
Michal Vasko5233e962020-08-14 14:26:20 +0200707 /* update printed bytes counter despite we actually printed just a hole */
708 out->printed += count;
709 out->func_printed += count;
710 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200711}
712
Michal Vasko66d99972020-06-29 13:37:42 +0200713LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200714ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200715{
Michal Vasko66d99972020-06-29 13:37:42 +0200716 LY_ERR ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200717
Michal Vasko08e9b112021-06-11 15:41:17 +0200718 assert(count);
719
Radek Krejcid3ca0632019-04-16 16:54:54 +0200720 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200721 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200722 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100723 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200724 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200725 case LY_OUT_FD:
726 case LY_OUT_FDSTREAM:
727 case LY_OUT_FILEPATH:
728 case LY_OUT_FILE:
729 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200730 if (out->buf_len < position + count) {
Michal Vasko5233e962020-08-14 14:26:20 +0200731 LOGMEM(NULL);
732 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200733 }
734
735 /* write into the hole */
736 memcpy(&out->buffered[position], buf, count);
737
738 /* decrease hole counter */
739 --out->hole_count;
740
741 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200742 /* all holes filled, we can write the buffer,
Michal Vasko5233e962020-08-14 14:26:20 +0200743 * printed bytes counter is updated by ly_write_() */
744 ret = ly_write_(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200745 out->buf_len = 0;
746 }
747 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200748 case LY_OUT_ERROR:
Michal Vasko5233e962020-08-14 14:26:20 +0200749 LOGINT(NULL);
750 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200751 }
752
Radek Krejci241f6b52020-05-21 18:13:49 +0200753 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100754 /* move the original file descriptor to the end of the output file */
755 lseek(out->method.fdstream.fd, 0, SEEK_END);
756 }
Michal Vasko66d99972020-06-29 13:37:42 +0200757 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200758}