blob: 3a7999d2e6ded751d16c3f07ea6c64657e2b97a0 [file] [log] [blame]
Radek Krejcid3ca0632019-04-16 16:54:54 +02001/**
Michal Vaskoafac7822020-10-20 14:22:26 +02002 * @file out.c
Radek Krejcid3ca0632019-04-16 16:54:54 +02003 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vaskoafac7822020-10-20 14:22:26 +02004 * @brief libyang output functions.
Radek Krejcid3ca0632019-04-16 16:54:54 +02005 *
Michal Vaskoafac7822020-10-20 14:22:26 +02006 * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
Radek Krejcid3ca0632019-04-16 16:54:54 +02007 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejcif8dc59a2020-11-25 13:47:44 +010016#define _POSIX_C_SOURCE 200809L /* strdup, vdprintf */
Radek Krejcid3ca0632019-04-16 16:54:54 +020017
Michal Vaskoafac7822020-10-20 14:22:26 +020018#include "out.h"
19#include "out_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020020
21#include <assert.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020022#include <errno.h>
23#include <stdarg.h>
Radek Krejci47fab892020-11-05 17:02:41 +010024#include <stdint.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020025#include <stdio.h>
26#include <stdlib.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020027#include <string.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020028#include <unistd.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020029
Radek Krejci535ea9f2020-05-29 16:01:05 +020030#include "common.h"
Michal Vasko5aa44c02020-06-29 11:47:02 +020031#include "compat.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020032#include "log.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020033#include "printer_data.h"
34#include "printer_internal.h"
Radek Krejci47fab892020-11-05 17:02:41 +010035#include "tree_data.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020036#include "tree_schema.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020037
Radek Krejci857189e2020-09-01 13:26:36 +020038ly_bool
Radek Krejci1deb5be2020-08-26 16:43:36 +020039ly_should_print(const struct lyd_node *node, uint32_t options)
Michal Vasko9b368d32020-02-14 13:53:31 +010040{
Michal Vasko5be03d62020-12-09 18:08:39 +010041 const struct lyd_node *elem, *op;
Michal Vasko9b368d32020-02-14 13:53:31 +010042
Radek Krejci7931b192020-06-25 17:05:03 +020043 if (options & LYD_PRINT_WD_TRIM) {
Michal Vasko9b368d32020-02-14 13:53:31 +010044 /* do not print default nodes */
45 if (node->flags & LYD_DEFAULT) {
46 /* implicit default node/NP container with only default nodes */
47 return 0;
48 } else if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci19611252020-10-04 13:54:53 +020049 if (lyd_is_default(node)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010050 /* explicit default node */
51 return 0;
52 }
53 }
Michal Vasko5be03d62020-12-09 18:08:39 +010054 } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK)) {
55 /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
56 for (op = node; op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = lyd_parent(op)) {}
57
58 if (!op && (node->schema->flags & LYS_CONFIG_W)) {
59 /* print only if it contains status data in its subtree */
60 LYD_TREE_DFS_BEGIN(node, elem) {
61 if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
62 if (elem->schema->flags & LYS_CONFIG_R) {
63 return 1;
64 }
Michal Vaskodb4f9e42020-06-01 17:29:56 +020065 }
Michal Vasko5be03d62020-12-09 18:08:39 +010066 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010067 }
Michal Vasko9b368d32020-02-14 13:53:31 +010068 }
69 return 0;
Radek Krejci7931b192020-06-25 17:05:03 +020070 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYD_PRINT_KEEPEMPTYCONT)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010071 /* avoid empty default containers */
Michal Vasko56daf732020-08-10 10:57:18 +020072 LYD_TREE_DFS_BEGIN(node, elem) {
Michal Vasko9b368d32020-02-14 13:53:31 +010073 if (elem->schema->nodetype != LYS_CONTAINER) {
74 return 1;
75 }
76 assert(elem->flags & LYD_DEFAULT);
Michal Vasko56daf732020-08-10 10:57:18 +020077 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010078 }
79 return 0;
80 }
81
82 return 1;
83}
84
Radek Krejci241f6b52020-05-21 18:13:49 +020085API LY_OUT_TYPE
86ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +010087{
Radek Krejci241f6b52020-05-21 18:13:49 +020088 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +010089 return out->type;
90}
91
Radek Krejci84ce7b12020-06-11 17:28:25 +020092API LY_ERR
Michal Vaskoce2b8742020-08-24 13:20:25 +020093ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +010094{
Radek Krejci84ce7b12020-06-11 17:28:25 +020095 LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +010096
Radek Krejci84ce7b12020-06-11 17:28:25 +020097 *out = calloc(1, sizeof **out);
98 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +010099
Radek Krejci84ce7b12020-06-11 17:28:25 +0200100 (*out)->type = LY_OUT_CALLBACK;
101 (*out)->method.clb.func = writeclb;
Michal Vaskoce2b8742020-08-24 13:20:25 +0200102 (*out)->method.clb.arg = user_data;
Radek Krejcia5bba312020-01-09 15:41:20 +0100103
Radek Krejci84ce7b12020-06-11 17:28:25 +0200104 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100105}
106
Michal Vasko69730152020-10-09 16:30:07 +0200107API ly_write_clb
108ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
Radek Krejcia5bba312020-01-09 15:41:20 +0100109{
110 void *prev_clb;
111
Radek Krejci241f6b52020-05-21 18:13:49 +0200112 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100113
114 prev_clb = out->method.clb.func;
115
116 if (writeclb) {
117 out->method.clb.func = writeclb;
118 }
119
120 return prev_clb;
121}
122
123API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200124ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100125{
126 void *prev_arg;
127
Radek Krejci241f6b52020-05-21 18:13:49 +0200128 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100129
130 prev_arg = out->method.clb.arg;
131
132 if (arg) {
133 out->method.clb.arg = arg;
134 }
135
136 return prev_arg;
137}
138
Radek Krejci84ce7b12020-06-11 17:28:25 +0200139API LY_ERR
140ly_out_new_fd(int fd, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100141{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200142 LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100143
Radek Krejci84ce7b12020-06-11 17:28:25 +0200144 *out = calloc(1, sizeof **out);
145 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200146 (*out)->type = LY_OUT_FD;
147 (*out)->method.fd = fd;
Radek Krejcia5bba312020-01-09 15:41:20 +0100148
Radek Krejci84ce7b12020-06-11 17:28:25 +0200149 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100150}
151
152API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200153ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100154{
155 int prev_fd;
156
Radek Krejci241f6b52020-05-21 18:13:49 +0200157 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100158
Radek Krejci241f6b52020-05-21 18:13:49 +0200159 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100160 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200161 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100162 prev_fd = out->method.fd;
163 }
164
165 if (fd != -1) {
166 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200167 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100168 int streamfd;
169 FILE *stream;
170
171 streamfd = dup(fd);
172 if (streamfd < 0) {
173 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
174 return -1;
175 }
176 stream = fdopen(streamfd, "a");
177 if (!stream) {
178 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
179 close(streamfd);
180 return -1;
181 }
182 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
183 fclose(out->method.fdstream.f);
184 out->method.fdstream.f = stream;
185 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200186 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100187 out->method.fd = fd;
188 }
189 }
190
191 return prev_fd;
192}
193
Radek Krejci84ce7b12020-06-11 17:28:25 +0200194API LY_ERR
195ly_out_new_file(FILE *f, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100196{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200197 LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100198
Radek Krejci84ce7b12020-06-11 17:28:25 +0200199 *out = calloc(1, sizeof **out);
200 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100201
Radek Krejci84ce7b12020-06-11 17:28:25 +0200202 (*out)->type = LY_OUT_FILE;
203 (*out)->method.f = f;
Radek Krejcia5bba312020-01-09 15:41:20 +0100204
Radek Krejci84ce7b12020-06-11 17:28:25 +0200205 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100206}
207
208API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200209ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100210{
211 FILE *prev_f;
212
Radek Krejci241f6b52020-05-21 18:13:49 +0200213 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100214
215 prev_f = out->method.f;
216
217 if (f) {
218 out->method.f = f;
219 }
220
221 return prev_f;
222}
223
Radek Krejci84ce7b12020-06-11 17:28:25 +0200224API LY_ERR
225ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100226{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200227 LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100228
Radek Krejci84ce7b12020-06-11 17:28:25 +0200229 *out = calloc(1, sizeof **out);
230 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100231
Radek Krejci84ce7b12020-06-11 17:28:25 +0200232 (*out)->type = LY_OUT_MEMORY;
233 (*out)->method.mem.buf = strp;
Radek Krejcia5bba312020-01-09 15:41:20 +0100234 if (!size) {
235 /* buffer is supposed to be allocated */
236 *strp = NULL;
237 } else if (*strp) {
238 /* there is already buffer to use */
Radek Krejci84ce7b12020-06-11 17:28:25 +0200239 (*out)->method.mem.size = size;
Radek Krejcia5bba312020-01-09 15:41:20 +0100240 }
241
Radek Krejci84ce7b12020-06-11 17:28:25 +0200242 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100243}
244
245char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200246ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100247{
248 char *data;
249
Radek Krejci241f6b52020-05-21 18:13:49 +0200250 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100251
252 data = *out->method.mem.buf;
253
254 if (strp) {
255 out->method.mem.buf = strp;
256 out->method.mem.len = out->method.mem.size = 0;
257 out->printed = 0;
258 if (!size) {
259 /* buffer is supposed to be allocated */
260 *strp = NULL;
261 } else if (*strp) {
262 /* there is already buffer to use */
263 out->method.mem.size = size;
264 }
265 }
266
267 return data;
268}
269
270API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200271ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100272{
273 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
274
Michal Vaskod989ba02020-08-24 10:59:24 +0200275 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200276 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100277 LOGINT(NULL);
278 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200279 case LY_OUT_FD:
Michal Vasko69730152020-10-09 16:30:07 +0200280 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100281 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
282 return LY_ESYS;
283 }
Michal Vasko69730152020-10-09 16:30:07 +0200284 if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200285 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
286 return LY_ESYS;
287 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100288 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200289 case LY_OUT_FDSTREAM:
290 case LY_OUT_FILE:
291 case LY_OUT_FILEPATH:
Michal Vasko69730152020-10-09 16:30:07 +0200292 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100293 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
294 return LY_ESYS;
295 }
Michal Vasko69730152020-10-09 16:30:07 +0200296 if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200297 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
298 return LY_ESYS;
299 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100300 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200301 case LY_OUT_MEMORY:
Radek Krejcic5a12e12020-05-27 17:09:59 +0200302 if (out->method.mem.buf && *out->method.mem.buf) {
303 memset(*out->method.mem.buf, 0, out->method.mem.len);
304 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100305 out->printed = 0;
306 out->method.mem.len = 0;
307 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200308 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100309 /* nothing to do (not seekable) */
310 break;
311 }
312
313 return LY_SUCCESS;
314}
315
Radek Krejci84ce7b12020-06-11 17:28:25 +0200316API LY_ERR
317ly_out_new_filepath(const char *filepath, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100318{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200319 LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100320
Radek Krejci84ce7b12020-06-11 17:28:25 +0200321 *out = calloc(1, sizeof **out);
322 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100323
Radek Krejci84ce7b12020-06-11 17:28:25 +0200324 (*out)->type = LY_OUT_FILEPATH;
325 (*out)->method.fpath.f = fopen(filepath, "w");
326 if (!(*out)->method.fpath.f) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100327 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
Radek Krejcif6923e82020-07-02 16:36:53 +0200328 free(*out);
329 *out = NULL;
Radek Krejci84ce7b12020-06-11 17:28:25 +0200330 return LY_ESYS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100331 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200332 (*out)->method.fpath.filepath = strdup(filepath);
333 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100334}
335
336API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200337ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100338{
339 FILE *f;
340
Radek Krejci241f6b52020-05-21 18:13:49 +0200341 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100342
343 if (!filepath) {
344 return out->method.fpath.filepath;
345 }
346
347 /* replace filepath */
348 f = out->method.fpath.f;
349 out->method.fpath.f = fopen(filepath, "w");
350 if (!out->method.fpath.f) {
351 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
352 out->method.fpath.f = f;
Michal Vasko69730152020-10-09 16:30:07 +0200353 return (void *)-1;
Radek Krejcia5bba312020-01-09 15:41:20 +0100354 }
355 fclose(f);
356 free(out->method.fpath.filepath);
357 out->method.fpath.filepath = strdup(filepath);
358
359 return NULL;
360}
361
362API void
Radek Krejci857189e2020-09-01 13:26:36 +0200363ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100364{
365 if (!out) {
366 return;
367 }
368
369 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200370 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100371 if (clb_arg_destructor) {
372 clb_arg_destructor(out->method.clb.arg);
373 }
374 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200375 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100376 fclose(out->method.fdstream.f);
377 if (destroy) {
378 close(out->method.fdstream.fd);
379 }
380 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200381 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100382 if (destroy) {
383 close(out->method.fd);
384 }
385 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200386 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100387 if (destroy) {
388 fclose(out->method.f);
389 }
390 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200391 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100392 if (destroy) {
393 free(*out->method.mem.buf);
394 }
395 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200396 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100397 free(out->method.fpath.filepath);
Radek Krejci2aae3752020-05-27 18:16:30 +0200398 fclose(out->method.fpath.f);
Radek Krejcia5bba312020-01-09 15:41:20 +0100399 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200400 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100401 LOGINT(NULL);
402 }
Michal Vasko159b8872020-11-18 18:14:16 +0100403
404 free(out->buffered);
Radek Krejcia5bba312020-01-09 15:41:20 +0100405 free(out);
406}
407
Michal Vasko5233e962020-08-14 14:26:20 +0200408static LY_ERR
409ly_vprint_(struct ly_out *out, const char *format, va_list ap)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200410{
Michal Vasko5233e962020-08-14 14:26:20 +0200411 LY_ERR ret;
412 int written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200413 char *msg = NULL, *aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200414
415 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200416 case LY_OUT_FD:
Michal Vasko5233e962020-08-14 14:26:20 +0200417 written = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200418 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200419 case LY_OUT_FDSTREAM:
420 case LY_OUT_FILEPATH:
421 case LY_OUT_FILE:
Michal Vasko5233e962020-08-14 14:26:20 +0200422 written = vfprintf(out->method.f, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200423 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200424 case LY_OUT_MEMORY:
Michal Vasko5233e962020-08-14 14:26:20 +0200425 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200426 break;
427 }
Michal Vasko5233e962020-08-14 14:26:20 +0200428 if (out->method.mem.len + written + 1 > out->method.mem.size) {
429 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200430 if (!aux) {
431 out->method.mem.buf = NULL;
432 out->method.mem.len = 0;
433 out->method.mem.size = 0;
434 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200435 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200436 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100437 *out->method.mem.buf = aux;
Michal Vasko5233e962020-08-14 14:26:20 +0200438 out->method.mem.size = out->method.mem.len + written + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200439 }
Michal Vasko5233e962020-08-14 14:26:20 +0200440 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
441 out->method.mem.len += written;
Radek Krejcia5bba312020-01-09 15:41:20 +0100442 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200443 free(msg);
444 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200445 case LY_OUT_CALLBACK:
Michal Vasko5233e962020-08-14 14:26:20 +0200446 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200447 break;
448 }
Michal Vasko5233e962020-08-14 14:26:20 +0200449 written = out->method.clb.func(out->method.clb.arg, msg, written);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200450 free(msg);
451 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200452 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100453 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200454 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200455 }
456
Michal Vasko5233e962020-08-14 14:26:20 +0200457 if (written < 0) {
458 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
459 written = 0;
460 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200461 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200462 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100463 /* move the original file descriptor to the end of the output file */
464 lseek(out->method.fdstream.fd, 0, SEEK_END);
465 }
Michal Vasko5233e962020-08-14 14:26:20 +0200466 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200467 }
Michal Vasko5233e962020-08-14 14:26:20 +0200468
469 out->printed += written;
470 out->func_printed += written;
471 return ret;
472}
473
474LY_ERR
475ly_print_(struct ly_out *out, const char *format, ...)
476{
477 LY_ERR ret;
478 va_list ap;
479
480 va_start(ap, format);
481 ret = ly_vprint_(out, format, ap);
482 va_end(ap);
483
484 return ret;
485}
486
487API LY_ERR
488ly_print(struct ly_out *out, const char *format, ...)
489{
490 LY_ERR ret;
491 va_list ap;
492
493 out->func_printed = 0;
494
495 va_start(ap, format);
496 ret = ly_vprint_(out, format, ap);
497 va_end(ap);
498
499 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200500}
501
Radek Krejci2aae3752020-05-27 18:16:30 +0200502API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200503ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200504{
505 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200506 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100507 /* move the original file descriptor to the end of the output file */
508 lseek(out->method.fdstream.fd, 0, SEEK_END);
509 fflush(out->method.fdstream.f);
510 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200511 case LY_OUT_FILEPATH:
512 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200513 fflush(out->method.f);
514 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200515 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200516 fsync(out->method.fd);
517 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200518 case LY_OUT_MEMORY:
519 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200520 /* nothing to do */
521 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200522 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100523 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200524 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200525
526 free(out->buffered);
527 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200528}
529
Michal Vasko5233e962020-08-14 14:26:20 +0200530LY_ERR
531ly_write_(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200532{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200533 LY_ERR ret = LY_SUCCESS;
534 size_t written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200535
536 if (out->hole_count) {
537 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200538 if (out->buf_len + len > out->buf_size) {
539 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200540 if (!out->buffered) {
541 out->buf_len = 0;
542 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200543 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200544 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200545 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200546 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200547 }
548
Radek Krejcie7b95092019-05-15 11:03:07 +0200549 memcpy(&out->buffered[out->buf_len], buf, len);
550 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 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100570 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
Radek Krejcie7b95092019-05-15 11:03:07 +0200571 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100572 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200573
Michal Vasko5233e962020-08-14 14:26:20 +0200574 written = len;
575 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200576 case LY_OUT_FD: {
577 ssize_t r;
578 r = write(out->method.fd, buf, len);
579 if (r < 0) {
580 ret = LY_ESYS;
581 } else {
582 written = (size_t)r;
583 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200584 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200585 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200586 case LY_OUT_FDSTREAM:
587 case LY_OUT_FILEPATH:
588 case LY_OUT_FILE:
Michal Vasko63f3d842020-07-08 10:10:14 +0200589 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200590 if (written != len) {
591 ret = LY_ESYS;
592 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200593 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200594 case LY_OUT_CALLBACK: {
595 ssize_t r;
596 r = out->method.clb.func(out->method.clb.arg, buf, len);
597 if (r < 0) {
598 ret = LY_ESYS;
599 } else {
600 written = (size_t)r;
601 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200602 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200603 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200604 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100605 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200606 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200607 }
608
Radek Krejci1deb5be2020-08-26 16:43:36 +0200609 if (ret) {
Michal Vasko69730152020-10-09 16:30:07 +0200610 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200611 ret = LY_SUCCESS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200612 goto repeat;
613 }
Michal Vasko5233e962020-08-14 14:26:20 +0200614 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
615 written = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200616 } else if ((size_t)written != len) {
Michal Vasko5233e962020-08-14 14:26:20 +0200617 LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
Michal Vasko69730152020-10-09 16:30:07 +0200618 len - (size_t)written, len);
Michal Vasko5233e962020-08-14 14:26:20 +0200619 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200620 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200621 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100622 /* move the original file descriptor to the end of the output file */
623 lseek(out->method.fdstream.fd, 0, SEEK_END);
624 }
Michal Vasko5233e962020-08-14 14:26:20 +0200625 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200626 }
Michal Vasko5233e962020-08-14 14:26:20 +0200627
628 out->printed += written;
629 out->func_printed += written;
630 return ret;
631}
632
633API LY_ERR
634ly_write(struct ly_out *out, const char *buf, size_t len)
635{
636 out->func_printed = 0;
637
638 return ly_write_(out, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200639}
640
Michal Vasko63f3d842020-07-08 10:10:14 +0200641API size_t
642ly_out_printed(const struct ly_out *out)
643{
644 return out->func_printed;
645}
646
Michal Vasko5233e962020-08-14 14:26:20 +0200647LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200648ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200649{
650 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200651 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200652 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100653 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
654 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200655 out->method.mem.len = 0;
656 out->method.mem.size = 0;
Michal Vasko5233e962020-08-14 14:26:20 +0200657 LOGMEM(NULL);
658 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200659 }
660 out->method.mem.size = out->method.mem.len + count;
661 }
662
663 /* save the current position */
664 *position = out->method.mem.len;
665
666 /* skip the memory */
667 out->method.mem.len += count;
668 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200669 case LY_OUT_FD:
670 case LY_OUT_FDSTREAM:
671 case LY_OUT_FILEPATH:
672 case LY_OUT_FILE:
673 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200674 /* buffer the hole */
675 if (out->buf_len + count > out->buf_size) {
676 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
677 if (!out->buffered) {
678 out->buf_len = 0;
679 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200680 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200681 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200682 }
683 out->buf_size = out->buf_len + count;
684 }
685
686 /* save the current position */
687 *position = out->buf_len;
688
689 /* skip the memory */
690 out->buf_len += count;
691
692 /* increase hole counter */
693 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100694 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200695 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100696 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200697 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200698 }
699
Michal Vasko5233e962020-08-14 14:26:20 +0200700 /* update printed bytes counter despite we actually printed just a hole */
701 out->printed += count;
702 out->func_printed += count;
703 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200704}
705
Michal Vasko66d99972020-06-29 13:37:42 +0200706LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200707ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200708{
Michal Vasko66d99972020-06-29 13:37:42 +0200709 LY_ERR ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200710
711 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200712 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200713 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100714 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200715 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200716 case LY_OUT_FD:
717 case LY_OUT_FDSTREAM:
718 case LY_OUT_FILEPATH:
719 case LY_OUT_FILE:
720 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200721 if (out->buf_len < position + count) {
Michal Vasko5233e962020-08-14 14:26:20 +0200722 LOGMEM(NULL);
723 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200724 }
725
726 /* write into the hole */
727 memcpy(&out->buffered[position], buf, count);
728
729 /* decrease hole counter */
730 --out->hole_count;
731
732 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200733 /* all holes filled, we can write the buffer,
Michal Vasko5233e962020-08-14 14:26:20 +0200734 * printed bytes counter is updated by ly_write_() */
735 ret = ly_write_(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200736 out->buf_len = 0;
737 }
738 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200739 case LY_OUT_ERROR:
Michal Vasko5233e962020-08-14 14:26:20 +0200740 LOGINT(NULL);
741 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200742 }
743
Radek Krejci241f6b52020-05-21 18:13:49 +0200744 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100745 /* move the original file descriptor to the end of the output file */
746 lseek(out->method.fdstream.fd, 0, SEEK_END);
747 }
Michal Vasko66d99972020-06-29 13:37:42 +0200748 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200749}