blob: 174f48e94dd242837dcfcd7919b81745c1a735f2 [file] [log] [blame]
Radek Krejcid3ca0632019-04-16 16:54:54 +02001/**
2 * @file printer.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
Radek Krejcie7b95092019-05-15 11:03:07 +02004 * @brief Generic libyang printers functions.
Radek Krejcid3ca0632019-04-16 16:54:54 +02005 *
6 * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
7 *
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 Krejcid3ca0632019-04-16 16:54:54 +020016
Radek Krejci535ea9f2020-05-29 16:01:05 +020017#include "printer.h"
18
19#include <assert.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020020#include <errno.h>
21#include <stdarg.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020022#include <stdio.h>
23#include <stdlib.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020024#include <string.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020025#include <unistd.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020026
Radek Krejci535ea9f2020-05-29 16:01:05 +020027#include "common.h"
Michal Vasko5aa44c02020-06-29 11:47:02 +020028#include "compat.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020029#include "log.h"
Michal Vasko9b368d32020-02-14 13:53:31 +010030#include "plugins_types.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020031#include "printer_data.h"
32#include "printer_internal.h"
33#include "tree.h"
34#include "tree_schema.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020035
36/**
37 * @brief informational structure shared by printers
38 */
39struct ext_substmt_info_s ext_substmt_info[] = {
Radek Krejci0f969882020-08-21 16:56:47 +020040 {NULL, NULL, 0}, /**< LYEXT_SUBSTMT_SELF */
41 {"argument", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ARGUMENT */
42 {"base", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BASE */
43 {"belongs-to", "module", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BELONGSTO */
44 {"contact", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_CONTACT */
45 {"default", "value", 0}, /**< LYEXT_SUBSTMT_DEFAULT */
46 {"description", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_DESCRIPTION */
47 {"error-app-tag", "value", 0}, /**< LYEXT_SUBSTMT_ERRTAG */
48 {"error-message", "value", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ERRMSG */
49 {"key", "value", 0}, /**< LYEXT_SUBSTMT_KEY */
50 {"namespace", "uri", 0}, /**< LYEXT_SUBSTMT_NAMESPACE */
51 {"organization", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ORGANIZATION */
52 {"path", "value", 0}, /**< LYEXT_SUBSTMT_PATH */
53 {"prefix", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_PREFIX */
54 {"presence", "value", 0}, /**< LYEXT_SUBSTMT_PRESENCE */
55 {"reference", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_REFERENCE */
56 {"revision-date", "date", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REVISIONDATE */
57 {"units", "name", 0}, /**< LYEXT_SUBSTMT_UNITS */
58 {"value", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VALUE */
59 {"yang-version", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VERSION */
60 {"modifier", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MODIFIER */
61 {"require-instance", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REQINST */
62 {"yin-element", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_YINELEM */
63 {"config", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_CONFIG */
64 {"mandatory", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MANDATORY */
65 {"ordered-by", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ORDEREDBY */
66 {"status", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_STATUS */
67 {"fraction-digits", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_DIGITS */
68 {"max-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MAX */
69 {"min-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MIN */
70 {"position", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_POSITION */
71 {"unique", "tag", 0}, /**< LYEXT_SUBSTMT_UNIQUE */
Radek Krejcid3ca0632019-04-16 16:54:54 +020072};
73
Radek Krejci857189e2020-09-01 13:26:36 +020074ly_bool
Radek Krejci1deb5be2020-08-26 16:43:36 +020075ly_should_print(const struct lyd_node *node, uint32_t options)
Michal Vasko9b368d32020-02-14 13:53:31 +010076{
Michal Vasko56daf732020-08-10 10:57:18 +020077 const struct lyd_node *elem;
Michal Vasko9b368d32020-02-14 13:53:31 +010078
Radek Krejci7931b192020-06-25 17:05:03 +020079 if (options & LYD_PRINT_WD_TRIM) {
Michal Vasko9b368d32020-02-14 13:53:31 +010080 /* do not print default nodes */
81 if (node->flags & LYD_DEFAULT) {
82 /* implicit default node/NP container with only default nodes */
83 return 0;
84 } else if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci19611252020-10-04 13:54:53 +020085 if (lyd_is_default(node)) {
Michal Vasko9b368d32020-02-14 13:53:31 +010086 /* explicit default node */
87 return 0;
88 }
89 }
Radek Krejci7931b192020-06-25 17:05:03 +020090 } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
Radek Krejci241f6b52020-05-21 18:13:49 +020091 /* LYDP_WD_EXPLICIT
Michal Vasko9b368d32020-02-14 13:53:31 +010092 * - print only if it contains status data in its subtree */
Michal Vasko56daf732020-08-10 10:57:18 +020093 LYD_TREE_DFS_BEGIN(node, elem) {
Michal Vaskodb4f9e42020-06-01 17:29:56 +020094 if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
95 if (elem->schema->flags & LYS_CONFIG_R) {
96 return 1;
97 }
Michal Vasko9b368d32020-02-14 13:53:31 +010098 }
Michal Vasko56daf732020-08-10 10:57:18 +020099 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +0100100 }
101 return 0;
Radek Krejci7931b192020-06-25 17:05:03 +0200102 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYD_PRINT_KEEPEMPTYCONT)) {
Michal Vasko9b368d32020-02-14 13:53:31 +0100103 /* avoid empty default containers */
Michal Vasko56daf732020-08-10 10:57:18 +0200104 LYD_TREE_DFS_BEGIN(node, elem) {
Michal Vasko9b368d32020-02-14 13:53:31 +0100105 if (elem->schema->nodetype != LYS_CONTAINER) {
106 return 1;
107 }
108 assert(elem->flags & LYD_DEFAULT);
Michal Vasko56daf732020-08-10 10:57:18 +0200109 LYD_TREE_DFS_END(node, elem)
Michal Vasko9b368d32020-02-14 13:53:31 +0100110 }
111 return 0;
112 }
113
114 return 1;
115}
116
Radek Krejci241f6b52020-05-21 18:13:49 +0200117API LY_OUT_TYPE
118ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100119{
Radek Krejci241f6b52020-05-21 18:13:49 +0200120 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +0100121 return out->type;
122}
123
Radek Krejci84ce7b12020-06-11 17:28:25 +0200124API LY_ERR
Michal Vaskoce2b8742020-08-24 13:20:25 +0200125ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100126{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200127 LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100128
Radek Krejci84ce7b12020-06-11 17:28:25 +0200129 *out = calloc(1, sizeof **out);
130 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100131
Radek Krejci84ce7b12020-06-11 17:28:25 +0200132 (*out)->type = LY_OUT_CALLBACK;
133 (*out)->method.clb.func = writeclb;
Michal Vaskoce2b8742020-08-24 13:20:25 +0200134 (*out)->method.clb.arg = user_data;
Radek Krejcia5bba312020-01-09 15:41:20 +0100135
Radek Krejci84ce7b12020-06-11 17:28:25 +0200136 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100137}
138
Michal Vasko69730152020-10-09 16:30:07 +0200139API ly_write_clb
140ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
Radek Krejcia5bba312020-01-09 15:41:20 +0100141{
142 void *prev_clb;
143
Radek Krejci241f6b52020-05-21 18:13:49 +0200144 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100145
146 prev_clb = out->method.clb.func;
147
148 if (writeclb) {
149 out->method.clb.func = writeclb;
150 }
151
152 return prev_clb;
153}
154
155API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200156ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100157{
158 void *prev_arg;
159
Radek Krejci241f6b52020-05-21 18:13:49 +0200160 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100161
162 prev_arg = out->method.clb.arg;
163
164 if (arg) {
165 out->method.clb.arg = arg;
166 }
167
168 return prev_arg;
169}
170
Radek Krejci84ce7b12020-06-11 17:28:25 +0200171API LY_ERR
172ly_out_new_fd(int fd, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100173{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200174 LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100175
Radek Krejci84ce7b12020-06-11 17:28:25 +0200176 *out = calloc(1, sizeof **out);
177 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200178 (*out)->type = LY_OUT_FD;
179 (*out)->method.fd = fd;
Radek Krejcia5bba312020-01-09 15:41:20 +0100180
Radek Krejci84ce7b12020-06-11 17:28:25 +0200181 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100182}
183
184API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200185ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100186{
187 int prev_fd;
188
Radek Krejci241f6b52020-05-21 18:13:49 +0200189 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100190
Radek Krejci241f6b52020-05-21 18:13:49 +0200191 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100192 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200193 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100194 prev_fd = out->method.fd;
195 }
196
197 if (fd != -1) {
198 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200199 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100200 int streamfd;
201 FILE *stream;
202
203 streamfd = dup(fd);
204 if (streamfd < 0) {
205 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
206 return -1;
207 }
208 stream = fdopen(streamfd, "a");
209 if (!stream) {
210 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
211 close(streamfd);
212 return -1;
213 }
214 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
215 fclose(out->method.fdstream.f);
216 out->method.fdstream.f = stream;
217 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200218 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100219 out->method.fd = fd;
220 }
221 }
222
223 return prev_fd;
224}
225
Radek Krejci84ce7b12020-06-11 17:28:25 +0200226API LY_ERR
227ly_out_new_file(FILE *f, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100228{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200229 LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100230
Radek Krejci84ce7b12020-06-11 17:28:25 +0200231 *out = calloc(1, sizeof **out);
232 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100233
Radek Krejci84ce7b12020-06-11 17:28:25 +0200234 (*out)->type = LY_OUT_FILE;
235 (*out)->method.f = f;
Radek Krejcia5bba312020-01-09 15:41:20 +0100236
Radek Krejci84ce7b12020-06-11 17:28:25 +0200237 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100238}
239
240API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200241ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100242{
243 FILE *prev_f;
244
Radek Krejci241f6b52020-05-21 18:13:49 +0200245 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100246
247 prev_f = out->method.f;
248
249 if (f) {
250 out->method.f = f;
251 }
252
253 return prev_f;
254}
255
Radek Krejci84ce7b12020-06-11 17:28:25 +0200256API LY_ERR
257ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100258{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200259 LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100260
Radek Krejci84ce7b12020-06-11 17:28:25 +0200261 *out = calloc(1, sizeof **out);
262 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100263
Radek Krejci84ce7b12020-06-11 17:28:25 +0200264 (*out)->type = LY_OUT_MEMORY;
265 (*out)->method.mem.buf = strp;
Radek Krejcia5bba312020-01-09 15:41:20 +0100266 if (!size) {
267 /* buffer is supposed to be allocated */
268 *strp = NULL;
269 } else if (*strp) {
270 /* there is already buffer to use */
Radek Krejci84ce7b12020-06-11 17:28:25 +0200271 (*out)->method.mem.size = size;
Radek Krejcia5bba312020-01-09 15:41:20 +0100272 }
273
Radek Krejci84ce7b12020-06-11 17:28:25 +0200274 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100275}
276
277char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200278ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100279{
280 char *data;
281
Radek Krejci241f6b52020-05-21 18:13:49 +0200282 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100283
284 data = *out->method.mem.buf;
285
286 if (strp) {
287 out->method.mem.buf = strp;
288 out->method.mem.len = out->method.mem.size = 0;
289 out->printed = 0;
290 if (!size) {
291 /* buffer is supposed to be allocated */
292 *strp = NULL;
293 } else if (*strp) {
294 /* there is already buffer to use */
295 out->method.mem.size = size;
296 }
297 }
298
299 return data;
300}
301
302API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200303ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100304{
305 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
306
Michal Vaskod989ba02020-08-24 10:59:24 +0200307 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200308 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100309 LOGINT(NULL);
310 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200311 case LY_OUT_FD:
Michal Vasko69730152020-10-09 16:30:07 +0200312 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100313 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
314 return LY_ESYS;
315 }
Michal Vasko69730152020-10-09 16:30:07 +0200316 if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200317 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
318 return LY_ESYS;
319 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100320 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200321 case LY_OUT_FDSTREAM:
322 case LY_OUT_FILE:
323 case LY_OUT_FILEPATH:
Michal Vasko69730152020-10-09 16:30:07 +0200324 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100325 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
326 return LY_ESYS;
327 }
Michal Vasko69730152020-10-09 16:30:07 +0200328 if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
Radek Krejcic5a12e12020-05-27 17:09:59 +0200329 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
330 return LY_ESYS;
331 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100332 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200333 case LY_OUT_MEMORY:
Radek Krejcic5a12e12020-05-27 17:09:59 +0200334 if (out->method.mem.buf && *out->method.mem.buf) {
335 memset(*out->method.mem.buf, 0, out->method.mem.len);
336 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100337 out->printed = 0;
338 out->method.mem.len = 0;
339 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200340 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100341 /* nothing to do (not seekable) */
342 break;
343 }
344
345 return LY_SUCCESS;
346}
347
Radek Krejci84ce7b12020-06-11 17:28:25 +0200348API LY_ERR
349ly_out_new_filepath(const char *filepath, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100350{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200351 LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100352
Radek Krejci84ce7b12020-06-11 17:28:25 +0200353 *out = calloc(1, sizeof **out);
354 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100355
Radek Krejci84ce7b12020-06-11 17:28:25 +0200356 (*out)->type = LY_OUT_FILEPATH;
357 (*out)->method.fpath.f = fopen(filepath, "w");
358 if (!(*out)->method.fpath.f) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100359 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
Radek Krejcif6923e82020-07-02 16:36:53 +0200360 free(*out);
361 *out = NULL;
Radek Krejci84ce7b12020-06-11 17:28:25 +0200362 return LY_ESYS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100363 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200364 (*out)->method.fpath.filepath = strdup(filepath);
365 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100366}
367
368API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200369ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100370{
371 FILE *f;
372
Radek Krejci241f6b52020-05-21 18:13:49 +0200373 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100374
375 if (!filepath) {
376 return out->method.fpath.filepath;
377 }
378
379 /* replace filepath */
380 f = out->method.fpath.f;
381 out->method.fpath.f = fopen(filepath, "w");
382 if (!out->method.fpath.f) {
383 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
384 out->method.fpath.f = f;
Michal Vasko69730152020-10-09 16:30:07 +0200385 return (void *)-1;
Radek Krejcia5bba312020-01-09 15:41:20 +0100386 }
387 fclose(f);
388 free(out->method.fpath.filepath);
389 out->method.fpath.filepath = strdup(filepath);
390
391 return NULL;
392}
393
394API void
Radek Krejci857189e2020-09-01 13:26:36 +0200395ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100396{
397 if (!out) {
398 return;
399 }
400
401 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200402 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100403 if (clb_arg_destructor) {
404 clb_arg_destructor(out->method.clb.arg);
405 }
406 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200407 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100408 fclose(out->method.fdstream.f);
409 if (destroy) {
410 close(out->method.fdstream.fd);
411 }
412 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200413 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100414 if (destroy) {
415 close(out->method.fd);
416 }
417 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200418 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100419 if (destroy) {
420 fclose(out->method.f);
421 }
422 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200423 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100424 if (destroy) {
425 free(*out->method.mem.buf);
426 }
427 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200428 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100429 free(out->method.fpath.filepath);
Radek Krejci2aae3752020-05-27 18:16:30 +0200430 fclose(out->method.fpath.f);
Radek Krejcia5bba312020-01-09 15:41:20 +0100431 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200432 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100433 LOGINT(NULL);
434 }
435 free(out);
436}
437
Michal Vasko5233e962020-08-14 14:26:20 +0200438static LY_ERR
439ly_vprint_(struct ly_out *out, const char *format, va_list ap)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200440{
Michal Vasko5233e962020-08-14 14:26:20 +0200441 LY_ERR ret;
442 int written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200443 char *msg = NULL, *aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200444
445 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200446 case LY_OUT_FD:
Michal Vasko5233e962020-08-14 14:26:20 +0200447 written = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200448 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200449 case LY_OUT_FDSTREAM:
450 case LY_OUT_FILEPATH:
451 case LY_OUT_FILE:
Michal Vasko5233e962020-08-14 14:26:20 +0200452 written = vfprintf(out->method.f, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200453 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200454 case LY_OUT_MEMORY:
Michal Vasko5233e962020-08-14 14:26:20 +0200455 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200456 break;
457 }
Michal Vasko5233e962020-08-14 14:26:20 +0200458 if (out->method.mem.len + written + 1 > out->method.mem.size) {
459 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200460 if (!aux) {
461 out->method.mem.buf = NULL;
462 out->method.mem.len = 0;
463 out->method.mem.size = 0;
464 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200465 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200466 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100467 *out->method.mem.buf = aux;
Michal Vasko5233e962020-08-14 14:26:20 +0200468 out->method.mem.size = out->method.mem.len + written + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200469 }
Michal Vasko5233e962020-08-14 14:26:20 +0200470 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
471 out->method.mem.len += written;
Radek Krejcia5bba312020-01-09 15:41:20 +0100472 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200473 free(msg);
474 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200475 case LY_OUT_CALLBACK:
Michal Vasko5233e962020-08-14 14:26:20 +0200476 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200477 break;
478 }
Michal Vasko5233e962020-08-14 14:26:20 +0200479 written = out->method.clb.func(out->method.clb.arg, msg, written);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200480 free(msg);
481 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200482 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100483 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200484 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200485 }
486
Michal Vasko5233e962020-08-14 14:26:20 +0200487 if (written < 0) {
488 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
489 written = 0;
490 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200491 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200492 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100493 /* move the original file descriptor to the end of the output file */
494 lseek(out->method.fdstream.fd, 0, SEEK_END);
495 }
Michal Vasko5233e962020-08-14 14:26:20 +0200496 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200497 }
Michal Vasko5233e962020-08-14 14:26:20 +0200498
499 out->printed += written;
500 out->func_printed += written;
501 return ret;
502}
503
504LY_ERR
505ly_print_(struct ly_out *out, const char *format, ...)
506{
507 LY_ERR ret;
508 va_list ap;
509
510 va_start(ap, format);
511 ret = ly_vprint_(out, format, ap);
512 va_end(ap);
513
514 return ret;
515}
516
517API LY_ERR
518ly_print(struct ly_out *out, const char *format, ...)
519{
520 LY_ERR ret;
521 va_list ap;
522
523 out->func_printed = 0;
524
525 va_start(ap, format);
526 ret = ly_vprint_(out, format, ap);
527 va_end(ap);
528
529 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200530}
531
Radek Krejci2aae3752020-05-27 18:16:30 +0200532API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200533ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200534{
535 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200536 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100537 /* move the original file descriptor to the end of the output file */
538 lseek(out->method.fdstream.fd, 0, SEEK_END);
539 fflush(out->method.fdstream.f);
540 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200541 case LY_OUT_FILEPATH:
542 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200543 fflush(out->method.f);
544 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200545 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200546 fsync(out->method.fd);
547 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200548 case LY_OUT_MEMORY:
549 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200550 /* nothing to do */
551 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200552 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100553 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200554 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200555
556 free(out->buffered);
557 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200558}
559
Michal Vasko5233e962020-08-14 14:26:20 +0200560LY_ERR
561ly_write_(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200562{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200563 LY_ERR ret = LY_SUCCESS;
564 size_t written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200565
566 if (out->hole_count) {
567 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200568 if (out->buf_len + len > out->buf_size) {
569 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200570 if (!out->buffered) {
571 out->buf_len = 0;
572 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200573 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200574 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200575 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200576 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200577 }
578
Radek Krejcie7b95092019-05-15 11:03:07 +0200579 memcpy(&out->buffered[out->buf_len], buf, len);
580 out->buf_len += len;
Michal Vasko5233e962020-08-14 14:26:20 +0200581
582 out->printed += len;
583 out->func_printed += len;
584 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200585 }
586
Radek Krejci897ad2e2019-04-29 16:43:07 +0200587repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200588 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200589 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200590 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100591 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
592 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200593 out->method.mem.len = 0;
594 out->method.mem.size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200595 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200596 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200597 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200598 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200599 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100600 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
Radek Krejcie7b95092019-05-15 11:03:07 +0200601 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100602 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200603
Michal Vasko5233e962020-08-14 14:26:20 +0200604 written = len;
605 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200606 case LY_OUT_FD: {
607 ssize_t r;
608 r = write(out->method.fd, buf, len);
609 if (r < 0) {
610 ret = LY_ESYS;
611 } else {
612 written = (size_t)r;
613 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200614 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200615 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200616 case LY_OUT_FDSTREAM:
617 case LY_OUT_FILEPATH:
618 case LY_OUT_FILE:
Michal Vasko63f3d842020-07-08 10:10:14 +0200619 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200620 if (written != len) {
621 ret = LY_ESYS;
622 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200623 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200624 case LY_OUT_CALLBACK: {
625 ssize_t r;
626 r = out->method.clb.func(out->method.clb.arg, buf, len);
627 if (r < 0) {
628 ret = LY_ESYS;
629 } else {
630 written = (size_t)r;
631 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200632 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200633 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200634 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100635 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200636 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200637 }
638
Radek Krejci1deb5be2020-08-26 16:43:36 +0200639 if (ret) {
Michal Vasko69730152020-10-09 16:30:07 +0200640 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200641 ret = LY_SUCCESS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200642 goto repeat;
643 }
Michal Vasko5233e962020-08-14 14:26:20 +0200644 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
645 written = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200646 } else if ((size_t)written != len) {
Michal Vasko5233e962020-08-14 14:26:20 +0200647 LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
Michal Vasko69730152020-10-09 16:30:07 +0200648 len - (size_t)written, len);
Michal Vasko5233e962020-08-14 14:26:20 +0200649 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200650 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200651 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100652 /* move the original file descriptor to the end of the output file */
653 lseek(out->method.fdstream.fd, 0, SEEK_END);
654 }
Michal Vasko5233e962020-08-14 14:26:20 +0200655 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200656 }
Michal Vasko5233e962020-08-14 14:26:20 +0200657
658 out->printed += written;
659 out->func_printed += written;
660 return ret;
661}
662
663API LY_ERR
664ly_write(struct ly_out *out, const char *buf, size_t len)
665{
666 out->func_printed = 0;
667
668 return ly_write_(out, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200669}
670
Michal Vasko63f3d842020-07-08 10:10:14 +0200671API size_t
672ly_out_printed(const struct ly_out *out)
673{
674 return out->func_printed;
675}
676
Michal Vasko5233e962020-08-14 14:26:20 +0200677LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200678ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200679{
680 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200681 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200682 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100683 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
684 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200685 out->method.mem.len = 0;
686 out->method.mem.size = 0;
Michal Vasko5233e962020-08-14 14:26:20 +0200687 LOGMEM(NULL);
688 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200689 }
690 out->method.mem.size = out->method.mem.len + count;
691 }
692
693 /* save the current position */
694 *position = out->method.mem.len;
695
696 /* skip the memory */
697 out->method.mem.len += count;
698 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200699 case LY_OUT_FD:
700 case LY_OUT_FDSTREAM:
701 case LY_OUT_FILEPATH:
702 case LY_OUT_FILE:
703 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200704 /* buffer the hole */
705 if (out->buf_len + count > out->buf_size) {
706 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
707 if (!out->buffered) {
708 out->buf_len = 0;
709 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200710 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200711 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200712 }
713 out->buf_size = out->buf_len + count;
714 }
715
716 /* save the current position */
717 *position = out->buf_len;
718
719 /* skip the memory */
720 out->buf_len += count;
721
722 /* increase hole counter */
723 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100724 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200725 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100726 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200727 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200728 }
729
Michal Vasko5233e962020-08-14 14:26:20 +0200730 /* update printed bytes counter despite we actually printed just a hole */
731 out->printed += count;
732 out->func_printed += count;
733 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200734}
735
Michal Vasko66d99972020-06-29 13:37:42 +0200736LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200737ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200738{
Michal Vasko66d99972020-06-29 13:37:42 +0200739 LY_ERR ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200740
741 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200742 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200743 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100744 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200745 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200746 case LY_OUT_FD:
747 case LY_OUT_FDSTREAM:
748 case LY_OUT_FILEPATH:
749 case LY_OUT_FILE:
750 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200751 if (out->buf_len < position + count) {
Michal Vasko5233e962020-08-14 14:26:20 +0200752 LOGMEM(NULL);
753 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200754 }
755
756 /* write into the hole */
757 memcpy(&out->buffered[position], buf, count);
758
759 /* decrease hole counter */
760 --out->hole_count;
761
762 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200763 /* all holes filled, we can write the buffer,
Michal Vasko5233e962020-08-14 14:26:20 +0200764 * printed bytes counter is updated by ly_write_() */
765 ret = ly_write_(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200766 out->buf_len = 0;
767 }
768 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200769 case LY_OUT_ERROR:
Michal Vasko5233e962020-08-14 14:26:20 +0200770 LOGINT(NULL);
771 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200772 }
773
Radek Krejci241f6b52020-05-21 18:13:49 +0200774 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100775 /* move the original file descriptor to the end of the output file */
776 lseek(out->method.fdstream.fd, 0, SEEK_END);
777 }
Michal Vasko66d99972020-06-29 13:37:42 +0200778 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200779}