blob: a37bc943688d90cd240004662422a9f97cacaf33 [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
Michal Vasko5aa44c02020-06-29 11:47:02 +020029#include "compat.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020030#include "log.h"
Michal Vasko8f702ee2024-02-20 15:44:24 +010031#include "ly_common.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
aPiecek61d76952021-08-30 10:28:04 +020036/**
37 * @brief Align the desired size to 1 KB.
38 */
39#define REALLOC_CHUNK(NEW_SIZE) \
40 NEW_SIZE + (1024 - (NEW_SIZE % 1024))
41
Michal Vasko8db584d2022-03-30 13:42:48 +020042LIBYANG_API_DEF ly_bool
43lyd_node_should_print(const struct lyd_node *node, uint32_t options)
Michal Vasko9b368d32020-02-14 13:53:31 +010044{
Michal Vaskob4fd37f2021-02-26 10:09:44 +010045 const struct lyd_node *elem;
Michal Vasko9b368d32020-02-14 13:53:31 +010046
Radek Krejci7931b192020-06-25 17:05:03 +020047 if (options & LYD_PRINT_WD_TRIM) {
Michal Vasko9b368d32020-02-14 13:53:31 +010048 /* do not print default nodes */
49 if (node->flags & LYD_DEFAULT) {
50 /* implicit default node/NP container with only default nodes */
51 return 0;
Michal Vaskobd777662021-07-09 13:58:40 +020052 } else if (node->schema && (node->schema->nodetype & LYD_NODE_TERM)) {
Radek Krejci19611252020-10-04 13:54:53 +020053 if (lyd_is_default(node)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010054 /* explicit default node */
55 return 0;
56 }
57 }
Michal Vasko1dd384e2021-07-08 13:59:55 +020058 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER)) {
59 if (options & LYD_PRINT_KEEPEMPTYCONT) {
60 /* explicit request to print */
61 return 1;
62 }
63
64 /* avoid empty default containers */
65 LYD_TREE_DFS_BEGIN(node, elem) {
Michal Vasko8db584d2022-03-30 13:42:48 +020066 if ((elem != node) && lyd_node_should_print(elem, options)) {
Michal Vasko1dd384e2021-07-08 13:59:55 +020067 return 1;
68 }
69 assert(elem->flags & LYD_DEFAULT);
70 LYD_TREE_DFS_END(node, elem)
71 }
72 return 0;
Michal Vasko0d5ea7e2021-02-26 10:10:15 +010073 } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
Michal Vasko5be03d62020-12-09 18:08:39 +010074 /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
Michal Vaskob4fd37f2021-02-26 10:09:44 +010075 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 +010076 /* print only if it contains status data in its subtree */
77 LYD_TREE_DFS_BEGIN(node, elem) {
78 if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
79 if (elem->schema->flags & LYS_CONFIG_R) {
80 return 1;
81 }
Michal Vaskodb4f9e42020-06-01 17:29:56 +020082 }
Michal Vasko5be03d62020-12-09 18:08:39 +010083 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +010084 }
Michal Vasko9b368d32020-02-14 13:53:31 +010085 }
86 return 0;
Michal Vasko9b368d32020-02-14 13:53:31 +010087 }
88
89 return 1;
90}
91
Jan Kundrátc53a7ec2021-12-09 16:01:19 +010092LIBYANG_API_DEF LY_OUT_TYPE
Radek Krejci241f6b52020-05-21 18:13:49 +020093ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +010094{
Radek Krejci241f6b52020-05-21 18:13:49 +020095 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +010096 return out->type;
97}
98
Jan Kundrátc53a7ec2021-12-09 16:01:19 +010099LIBYANG_API_DEF LY_ERR
Michal Vaskoce2b8742020-08-24 13:20:25 +0200100ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100101{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200102 LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100103
Radek Krejci84ce7b12020-06-11 17:28:25 +0200104 *out = calloc(1, sizeof **out);
105 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100106
Radek Krejci84ce7b12020-06-11 17:28:25 +0200107 (*out)->type = LY_OUT_CALLBACK;
108 (*out)->method.clb.func = writeclb;
Michal Vaskoce2b8742020-08-24 13:20:25 +0200109 (*out)->method.clb.arg = user_data;
Radek Krejcia5bba312020-01-09 15:41:20 +0100110
Radek Krejci84ce7b12020-06-11 17:28:25 +0200111 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100112}
113
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100114LIBYANG_API_DEF ly_write_clb
Michal Vasko69730152020-10-09 16:30:07 +0200115ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
Radek Krejcia5bba312020-01-09 15:41:20 +0100116{
Michal Vasko59e90fc2021-09-22 12:17:08 +0200117 ly_write_clb prev_clb;
Radek Krejcia5bba312020-01-09 15:41:20 +0100118
Radek Krejci241f6b52020-05-21 18:13:49 +0200119 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100120
121 prev_clb = out->method.clb.func;
122
123 if (writeclb) {
124 out->method.clb.func = writeclb;
125 }
126
127 return prev_clb;
128}
129
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100130LIBYANG_API_DEF void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200131ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100132{
133 void *prev_arg;
134
Radek Krejci241f6b52020-05-21 18:13:49 +0200135 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100136
137 prev_arg = out->method.clb.arg;
138
139 if (arg) {
140 out->method.clb.arg = arg;
141 }
142
143 return prev_arg;
144}
145
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100146LIBYANG_API_DEF LY_ERR
Radek Krejci84ce7b12020-06-11 17:28:25 +0200147ly_out_new_fd(int fd, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100148{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200149 LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100150
Radek Krejci84ce7b12020-06-11 17:28:25 +0200151 *out = calloc(1, sizeof **out);
152 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200153 (*out)->type = LY_OUT_FD;
154 (*out)->method.fd = fd;
Radek Krejcia5bba312020-01-09 15:41:20 +0100155
Radek Krejci84ce7b12020-06-11 17:28:25 +0200156 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100157}
158
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100159LIBYANG_API_DEF int
Radek Krejci241f6b52020-05-21 18:13:49 +0200160ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100161{
162 int prev_fd;
163
Radek Krejci241f6b52020-05-21 18:13:49 +0200164 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100165
Radek Krejci241f6b52020-05-21 18:13:49 +0200166 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100167 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200168 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100169 prev_fd = out->method.fd;
170 }
171
172 if (fd != -1) {
173 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200174 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100175 int streamfd;
176 FILE *stream;
177
178 streamfd = dup(fd);
179 if (streamfd < 0) {
180 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
181 return -1;
182 }
183 stream = fdopen(streamfd, "a");
184 if (!stream) {
185 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
186 close(streamfd);
187 return -1;
188 }
189 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
190 fclose(out->method.fdstream.f);
191 out->method.fdstream.f = stream;
192 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200193 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100194 out->method.fd = fd;
195 }
196 }
197
198 return prev_fd;
199}
200
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100201LIBYANG_API_DEF LY_ERR
Radek Krejci84ce7b12020-06-11 17:28:25 +0200202ly_out_new_file(FILE *f, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100203{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200204 LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100205
Radek Krejci84ce7b12020-06-11 17:28:25 +0200206 *out = calloc(1, sizeof **out);
207 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100208
Radek Krejci84ce7b12020-06-11 17:28:25 +0200209 (*out)->type = LY_OUT_FILE;
210 (*out)->method.f = f;
Radek Krejcia5bba312020-01-09 15:41:20 +0100211
Radek Krejci84ce7b12020-06-11 17:28:25 +0200212 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100213}
214
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100215LIBYANG_API_DEF FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200216ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100217{
218 FILE *prev_f;
219
Radek Krejci241f6b52020-05-21 18:13:49 +0200220 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100221
222 prev_f = out->method.f;
223
224 if (f) {
225 out->method.f = f;
226 }
227
228 return prev_f;
229}
230
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100231LIBYANG_API_DEF LY_ERR
Radek Krejci84ce7b12020-06-11 17:28:25 +0200232ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100233{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200234 LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100235
Radek Krejci84ce7b12020-06-11 17:28:25 +0200236 *out = calloc(1, sizeof **out);
237 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100238
Radek Krejci84ce7b12020-06-11 17:28:25 +0200239 (*out)->type = LY_OUT_MEMORY;
240 (*out)->method.mem.buf = strp;
Radek Krejcia5bba312020-01-09 15:41:20 +0100241 if (!size) {
242 /* buffer is supposed to be allocated */
243 *strp = NULL;
244 } else if (*strp) {
245 /* there is already buffer to use */
Radek Krejci84ce7b12020-06-11 17:28:25 +0200246 (*out)->method.mem.size = size;
Radek Krejcia5bba312020-01-09 15:41:20 +0100247 }
248
Radek Krejci84ce7b12020-06-11 17:28:25 +0200249 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100250}
251
252char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200253ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100254{
255 char *data;
256
Radek Krejci241f6b52020-05-21 18:13:49 +0200257 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100258
259 data = *out->method.mem.buf;
260
261 if (strp) {
262 out->method.mem.buf = strp;
263 out->method.mem.len = out->method.mem.size = 0;
264 out->printed = 0;
265 if (!size) {
266 /* buffer is supposed to be allocated */
267 *strp = NULL;
268 } else if (*strp) {
269 /* there is already buffer to use */
270 out->method.mem.size = size;
271 }
272 }
273
274 return data;
275}
276
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100277LIBYANG_API_DEF LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200278ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100279{
280 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
281
Michal Vaskod989ba02020-08-24 10:59:24 +0200282 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200283 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100284 LOGINT(NULL);
285 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200286 case LY_OUT_FD:
Michal Vasko69730152020-10-09 16:30:07 +0200287 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100288 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
289 return LY_ESYS;
290 }
Michal Vasko69730152020-10-09 16:30:07 +0200291 if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200292 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
293 return LY_ESYS;
294 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100295 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200296 case LY_OUT_FDSTREAM:
297 case LY_OUT_FILE:
298 case LY_OUT_FILEPATH:
Michal Vasko69730152020-10-09 16:30:07 +0200299 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100300 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
301 return LY_ESYS;
302 }
Michal Vasko69730152020-10-09 16:30:07 +0200303 if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200304 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
305 return LY_ESYS;
306 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100307 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200308 case LY_OUT_MEMORY:
Radek Krejcic5a12e12020-05-27 17:09:59 +0200309 if (out->method.mem.buf && *out->method.mem.buf) {
310 memset(*out->method.mem.buf, 0, out->method.mem.len);
311 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100312 out->printed = 0;
313 out->method.mem.len = 0;
314 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200315 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100316 /* nothing to do (not seekable) */
317 break;
318 }
319
320 return LY_SUCCESS;
321}
322
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100323LIBYANG_API_DEF LY_ERR
Radek Krejci84ce7b12020-06-11 17:28:25 +0200324ly_out_new_filepath(const char *filepath, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100325{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200326 LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100327
Radek Krejci84ce7b12020-06-11 17:28:25 +0200328 *out = calloc(1, sizeof **out);
329 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100330
Radek Krejci84ce7b12020-06-11 17:28:25 +0200331 (*out)->type = LY_OUT_FILEPATH;
Jan Kundrátb1aa77f2021-12-13 15:16:47 +0100332 (*out)->method.fpath.f = fopen(filepath, "wb");
Radek Krejci84ce7b12020-06-11 17:28:25 +0200333 if (!(*out)->method.fpath.f) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100334 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
Radek Krejcif6923e82020-07-02 16:36:53 +0200335 free(*out);
336 *out = NULL;
Radek Krejci84ce7b12020-06-11 17:28:25 +0200337 return LY_ESYS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100338 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200339 (*out)->method.fpath.filepath = strdup(filepath);
340 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100341}
342
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100343LIBYANG_API_DEF const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200344ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100345{
346 FILE *f;
347
Radek Krejci241f6b52020-05-21 18:13:49 +0200348 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100349
350 if (!filepath) {
351 return out->method.fpath.filepath;
352 }
353
354 /* replace filepath */
355 f = out->method.fpath.f;
Jan Kundrátb1aa77f2021-12-13 15:16:47 +0100356 out->method.fpath.f = fopen(filepath, "wb");
Radek Krejcia5bba312020-01-09 15:41:20 +0100357 if (!out->method.fpath.f) {
358 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
359 out->method.fpath.f = f;
Michal Vasko69730152020-10-09 16:30:07 +0200360 return (void *)-1;
Radek Krejcia5bba312020-01-09 15:41:20 +0100361 }
362 fclose(f);
363 free(out->method.fpath.filepath);
364 out->method.fpath.filepath = strdup(filepath);
365
366 return NULL;
367}
368
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100369LIBYANG_API_DEF void
Radek Krejci857189e2020-09-01 13:26:36 +0200370ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100371{
372 if (!out) {
373 return;
374 }
375
376 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200377 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100378 if (clb_arg_destructor) {
379 clb_arg_destructor(out->method.clb.arg);
380 }
381 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200382 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100383 fclose(out->method.fdstream.f);
384 if (destroy) {
385 close(out->method.fdstream.fd);
386 }
387 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200388 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100389 if (destroy) {
390 close(out->method.fd);
391 }
392 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200393 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100394 if (destroy) {
395 fclose(out->method.f);
396 }
397 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200398 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100399 if (destroy) {
400 free(*out->method.mem.buf);
401 }
402 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200403 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100404 free(out->method.fpath.filepath);
Radek Krejci2aae3752020-05-27 18:16:30 +0200405 fclose(out->method.fpath.f);
Radek Krejcia5bba312020-01-09 15:41:20 +0100406 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200407 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100408 LOGINT(NULL);
409 }
Michal Vasko159b8872020-11-18 18:14:16 +0100410
411 free(out->buffered);
Radek Krejcia5bba312020-01-09 15:41:20 +0100412 free(out);
413}
414
Michal Vasko5233e962020-08-14 14:26:20 +0200415static LY_ERR
416ly_vprint_(struct ly_out *out, const char *format, va_list ap)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200417{
Michal Vasko5233e962020-08-14 14:26:20 +0200418 LY_ERR ret;
419 int written = 0;
Barbaros Tokaogluffc05ea2023-08-12 15:19:11 +0300420 char *msg = NULL;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200421
422 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200423 case LY_OUT_FD:
Michal Vasko5233e962020-08-14 14:26:20 +0200424 written = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200425 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200426 case LY_OUT_FDSTREAM:
427 case LY_OUT_FILEPATH:
428 case LY_OUT_FILE:
Michal Vasko5233e962020-08-14 14:26:20 +0200429 written = vfprintf(out->method.f, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200430 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200431 case LY_OUT_MEMORY:
Michal Vasko5233e962020-08-14 14:26:20 +0200432 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200433 break;
434 }
Michal Vasko5233e962020-08-14 14:26:20 +0200435 if (out->method.mem.len + written + 1 > out->method.mem.size) {
Barbaros Tokaogluffc05ea2023-08-12 15:19:11 +0300436 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
437 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200438 out->method.mem.len = 0;
439 out->method.mem.size = 0;
optimden3fb41f32024-02-14 15:45:46 +0545440 free(msg);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200441 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200442 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200443 }
Michal Vasko5233e962020-08-14 14:26:20 +0200444 out->method.mem.size = out->method.mem.len + written + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200445 }
Michal Vasko08e9b112021-06-11 15:41:17 +0200446 if (written) {
447 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
448 }
Michal Vasko5233e962020-08-14 14:26:20 +0200449 out->method.mem.len += written;
Radek Krejcia5bba312020-01-09 15:41:20 +0100450 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200451 free(msg);
452 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200453 case LY_OUT_CALLBACK:
Michal Vasko5233e962020-08-14 14:26:20 +0200454 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200455 break;
456 }
Michal Vasko5233e962020-08-14 14:26:20 +0200457 written = out->method.clb.func(out->method.clb.arg, msg, written);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200458 free(msg);
459 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200460 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100461 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200462 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200463 }
464
Michal Vasko5233e962020-08-14 14:26:20 +0200465 if (written < 0) {
466 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
467 written = 0;
468 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200469 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200470 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100471 /* move the original file descriptor to the end of the output file */
472 lseek(out->method.fdstream.fd, 0, SEEK_END);
473 }
Michal Vasko5233e962020-08-14 14:26:20 +0200474 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200475 }
Michal Vasko5233e962020-08-14 14:26:20 +0200476
477 out->printed += written;
478 out->func_printed += written;
479 return ret;
480}
481
482LY_ERR
483ly_print_(struct ly_out *out, const char *format, ...)
484{
485 LY_ERR ret;
486 va_list ap;
487
488 va_start(ap, format);
489 ret = ly_vprint_(out, format, ap);
490 va_end(ap);
491
492 return ret;
493}
494
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100495LIBYANG_API_DEF LY_ERR
Michal Vasko5233e962020-08-14 14:26:20 +0200496ly_print(struct ly_out *out, const char *format, ...)
497{
498 LY_ERR ret;
499 va_list ap;
500
501 out->func_printed = 0;
502
503 va_start(ap, format);
504 ret = ly_vprint_(out, format, ap);
505 va_end(ap);
506
507 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200508}
509
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100510LIBYANG_API_DEF void
Radek Krejci241f6b52020-05-21 18:13:49 +0200511ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200512{
513 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200514 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100515 /* move the original file descriptor to the end of the output file */
516 lseek(out->method.fdstream.fd, 0, SEEK_END);
517 fflush(out->method.fdstream.f);
518 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200519 case LY_OUT_FILEPATH:
520 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200521 fflush(out->method.f);
522 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200523 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200524 fsync(out->method.fd);
525 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200526 case LY_OUT_MEMORY:
527 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200528 /* nothing to do */
529 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200530 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100531 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200532 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200533
534 free(out->buffered);
535 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200536}
537
Michal Vasko5233e962020-08-14 14:26:20 +0200538LY_ERR
539ly_write_(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200540{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200541 LY_ERR ret = LY_SUCCESS;
aPiecek61d76952021-08-30 10:28:04 +0200542 size_t written = 0, new_mem_size;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200543
544 if (out->hole_count) {
545 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200546 if (out->buf_len + len > out->buf_size) {
547 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200548 if (!out->buffered) {
549 out->buf_len = 0;
550 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200551 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200552 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200553 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200554 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200555 }
556
Michal Vasko08e9b112021-06-11 15:41:17 +0200557 if (len) {
558 memcpy(&out->buffered[out->buf_len], buf, len);
559 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200560 out->buf_len += len;
Michal Vasko5233e962020-08-14 14:26:20 +0200561
562 out->printed += len;
563 out->func_printed += len;
564 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200565 }
566
Radek Krejci897ad2e2019-04-29 16:43:07 +0200567repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200568 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200569 case LY_OUT_MEMORY:
aPiecek61d76952021-08-30 10:28:04 +0200570 new_mem_size = out->method.mem.len + len + 1;
571 if (new_mem_size > out->method.mem.size) {
572 new_mem_size = REALLOC_CHUNK(new_mem_size);
573 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, new_mem_size);
Radek Krejcia5bba312020-01-09 15:41:20 +0100574 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200575 out->method.mem.len = 0;
576 out->method.mem.size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200577 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200578 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200579 }
aPiecek61d76952021-08-30 10:28:04 +0200580 out->method.mem.size = new_mem_size;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200581 }
Michal Vasko08e9b112021-06-11 15:41:17 +0200582 if (len) {
583 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
584 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200585 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100586 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200587
Michal Vasko5233e962020-08-14 14:26:20 +0200588 written = len;
589 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200590 case LY_OUT_FD: {
591 ssize_t r;
Michal Vasko26bbb272022-08-02 14:54:33 +0200592
Radek Krejci1deb5be2020-08-26 16:43:36 +0200593 r = write(out->method.fd, 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_FDSTREAM:
602 case LY_OUT_FILEPATH:
603 case LY_OUT_FILE:
Michal Vasko63f3d842020-07-08 10:10:14 +0200604 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200605 if (written != len) {
606 ret = LY_ESYS;
607 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200608 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200609 case LY_OUT_CALLBACK: {
610 ssize_t r;
Michal Vasko26bbb272022-08-02 14:54:33 +0200611
Radek Krejci1deb5be2020-08-26 16:43:36 +0200612 r = out->method.clb.func(out->method.clb.arg, buf, len);
613 if (r < 0) {
614 ret = LY_ESYS;
615 } else {
616 written = (size_t)r;
617 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200618 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200619 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200620 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100621 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200622 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200623 }
624
Radek Krejci1deb5be2020-08-26 16:43:36 +0200625 if (ret) {
Michal Vasko69730152020-10-09 16:30:07 +0200626 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200627 ret = LY_SUCCESS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200628 goto repeat;
629 }
Michal Vasko5233e962020-08-14 14:26:20 +0200630 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
631 written = 0;
Michal Vasko7b3a00e2023-08-09 11:58:03 +0200632 } else if (written != len) {
633 LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %" PRIu32 " from %" PRIu32 " data).", __func__,
634 (uint32_t)(len - written), (uint32_t)len);
Michal Vasko5233e962020-08-14 14:26:20 +0200635 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200636 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200637 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100638 /* move the original file descriptor to the end of the output file */
639 lseek(out->method.fdstream.fd, 0, SEEK_END);
640 }
Michal Vasko5233e962020-08-14 14:26:20 +0200641 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200642 }
Michal Vasko5233e962020-08-14 14:26:20 +0200643
644 out->printed += written;
645 out->func_printed += written;
646 return ret;
647}
648
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100649LIBYANG_API_DEF LY_ERR
Michal Vasko5233e962020-08-14 14:26:20 +0200650ly_write(struct ly_out *out, const char *buf, size_t len)
651{
652 out->func_printed = 0;
653
654 return ly_write_(out, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200655}
656
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100657LIBYANG_API_DEF size_t
Michal Vasko63f3d842020-07-08 10:10:14 +0200658ly_out_printed(const struct ly_out *out)
659{
660 return out->func_printed;
661}
662
Michal Vasko5233e962020-08-14 14:26:20 +0200663LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200664ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200665{
666 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200667 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200668 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100669 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
670 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200671 out->method.mem.len = 0;
672 out->method.mem.size = 0;
Michal Vasko5233e962020-08-14 14:26:20 +0200673 LOGMEM(NULL);
674 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200675 }
676 out->method.mem.size = out->method.mem.len + count;
677 }
678
679 /* save the current position */
680 *position = out->method.mem.len;
681
682 /* skip the memory */
683 out->method.mem.len += count;
684 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200685 case LY_OUT_FD:
686 case LY_OUT_FDSTREAM:
687 case LY_OUT_FILEPATH:
688 case LY_OUT_FILE:
689 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200690 /* buffer the hole */
691 if (out->buf_len + count > out->buf_size) {
692 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
693 if (!out->buffered) {
694 out->buf_len = 0;
695 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200696 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200697 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200698 }
699 out->buf_size = out->buf_len + count;
700 }
701
702 /* save the current position */
703 *position = out->buf_len;
704
705 /* skip the memory */
706 out->buf_len += count;
707
708 /* increase hole counter */
709 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100710 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200711 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100712 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200713 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200714 }
715
Michal Vasko5233e962020-08-14 14:26:20 +0200716 /* update printed bytes counter despite we actually printed just a hole */
717 out->printed += count;
718 out->func_printed += count;
719 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200720}
721
Michal Vasko66d99972020-06-29 13:37:42 +0200722LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200723ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200724{
Michal Vasko66d99972020-06-29 13:37:42 +0200725 LY_ERR ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200726
Michal Vasko08e9b112021-06-11 15:41:17 +0200727 assert(count);
728
Radek Krejcid3ca0632019-04-16 16:54:54 +0200729 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200730 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200731 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100732 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200733 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200734 case LY_OUT_FD:
735 case LY_OUT_FDSTREAM:
736 case LY_OUT_FILEPATH:
737 case LY_OUT_FILE:
738 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200739 if (out->buf_len < position + count) {
Michal Vasko5233e962020-08-14 14:26:20 +0200740 LOGMEM(NULL);
741 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200742 }
743
744 /* write into the hole */
745 memcpy(&out->buffered[position], buf, count);
746
747 /* decrease hole counter */
748 --out->hole_count;
749
750 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200751 /* all holes filled, we can write the buffer,
Michal Vasko5233e962020-08-14 14:26:20 +0200752 * printed bytes counter is updated by ly_write_() */
753 ret = ly_write_(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200754 out->buf_len = 0;
755 }
756 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200757 case LY_OUT_ERROR:
Michal Vasko5233e962020-08-14 14:26:20 +0200758 LOGINT(NULL);
759 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200760 }
761
Radek Krejci241f6b52020-05-21 18:13:49 +0200762 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100763 /* move the original file descriptor to the end of the output file */
764 lseek(out->method.fdstream.fd, 0, SEEK_END);
765 }
Michal Vasko66d99972020-06-29 13:37:42 +0200766 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200767}