blob: dd48e51bdf166fa36f907781fa05fdeb6f5d088b [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 Vaskoce2b8742020-08-24 13:20:25 +0200139API ly_write_clb ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
Radek Krejcia5bba312020-01-09 15:41:20 +0100140{
141 void *prev_clb;
142
Radek Krejci241f6b52020-05-21 18:13:49 +0200143 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100144
145 prev_clb = out->method.clb.func;
146
147 if (writeclb) {
148 out->method.clb.func = writeclb;
149 }
150
151 return prev_clb;
152}
153
154API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200155ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100156{
157 void *prev_arg;
158
Radek Krejci241f6b52020-05-21 18:13:49 +0200159 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100160
161 prev_arg = out->method.clb.arg;
162
163 if (arg) {
164 out->method.clb.arg = arg;
165 }
166
167 return prev_arg;
168}
169
Radek Krejci84ce7b12020-06-11 17:28:25 +0200170API LY_ERR
171ly_out_new_fd(int fd, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100172{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200173 LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100174
Radek Krejci84ce7b12020-06-11 17:28:25 +0200175 *out = calloc(1, sizeof **out);
176 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200177 (*out)->type = LY_OUT_FD;
178 (*out)->method.fd = fd;
Radek Krejcia5bba312020-01-09 15:41:20 +0100179
Radek Krejci84ce7b12020-06-11 17:28:25 +0200180 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100181}
182
183API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200184ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100185{
186 int prev_fd;
187
Radek Krejci241f6b52020-05-21 18:13:49 +0200188 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100189
Radek Krejci241f6b52020-05-21 18:13:49 +0200190 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100191 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200192 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100193 prev_fd = out->method.fd;
194 }
195
196 if (fd != -1) {
197 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200198 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100199 int streamfd;
200 FILE *stream;
201
202 streamfd = dup(fd);
203 if (streamfd < 0) {
204 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
205 return -1;
206 }
207 stream = fdopen(streamfd, "a");
208 if (!stream) {
209 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
210 close(streamfd);
211 return -1;
212 }
213 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
214 fclose(out->method.fdstream.f);
215 out->method.fdstream.f = stream;
216 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200217 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100218 out->method.fd = fd;
219 }
220 }
221
222 return prev_fd;
223}
224
Radek Krejci84ce7b12020-06-11 17:28:25 +0200225API LY_ERR
226ly_out_new_file(FILE *f, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100227{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200228 LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100229
Radek Krejci84ce7b12020-06-11 17:28:25 +0200230 *out = calloc(1, sizeof **out);
231 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100232
Radek Krejci84ce7b12020-06-11 17:28:25 +0200233 (*out)->type = LY_OUT_FILE;
234 (*out)->method.f = f;
Radek Krejcia5bba312020-01-09 15:41:20 +0100235
Radek Krejci84ce7b12020-06-11 17:28:25 +0200236 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100237}
238
239API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200240ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100241{
242 FILE *prev_f;
243
Radek Krejci241f6b52020-05-21 18:13:49 +0200244 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100245
246 prev_f = out->method.f;
247
248 if (f) {
249 out->method.f = f;
250 }
251
252 return prev_f;
253}
254
Radek Krejci84ce7b12020-06-11 17:28:25 +0200255API LY_ERR
256ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100257{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200258 LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100259
Radek Krejci84ce7b12020-06-11 17:28:25 +0200260 *out = calloc(1, sizeof **out);
261 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100262
Radek Krejci84ce7b12020-06-11 17:28:25 +0200263 (*out)->type = LY_OUT_MEMORY;
264 (*out)->method.mem.buf = strp;
Radek Krejcia5bba312020-01-09 15:41:20 +0100265 if (!size) {
266 /* buffer is supposed to be allocated */
267 *strp = NULL;
268 } else if (*strp) {
269 /* there is already buffer to use */
Radek Krejci84ce7b12020-06-11 17:28:25 +0200270 (*out)->method.mem.size = size;
Radek Krejcia5bba312020-01-09 15:41:20 +0100271 }
272
Radek Krejci84ce7b12020-06-11 17:28:25 +0200273 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100274}
275
276char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200277ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100278{
279 char *data;
280
Radek Krejci241f6b52020-05-21 18:13:49 +0200281 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100282
283 data = *out->method.mem.buf;
284
285 if (strp) {
286 out->method.mem.buf = strp;
287 out->method.mem.len = out->method.mem.size = 0;
288 out->printed = 0;
289 if (!size) {
290 /* buffer is supposed to be allocated */
291 *strp = NULL;
292 } else if (*strp) {
293 /* there is already buffer to use */
294 out->method.mem.size = size;
295 }
296 }
297
298 return data;
299}
300
301API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200302ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100303{
304 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
305
Michal Vaskod989ba02020-08-24 10:59:24 +0200306 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200307 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100308 LOGINT(NULL);
309 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200310 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100311 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && errno != ESPIPE) {
312 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
313 return LY_ESYS;
314 }
Radek Krejcic5a12e12020-05-27 17:09:59 +0200315 if (errno != ESPIPE && ftruncate(out->method.fd, 0) == -1) {
316 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
317 return LY_ESYS;
318 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100319 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200320 case LY_OUT_FDSTREAM:
321 case LY_OUT_FILE:
322 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100323 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && errno != ESPIPE) {
324 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
325 return LY_ESYS;
326 }
Radek Krejcic5a12e12020-05-27 17:09:59 +0200327 if (errno != ESPIPE && ftruncate(fileno(out->method.f), 0) == -1) {
328 LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
329 return LY_ESYS;
330 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100331 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200332 case LY_OUT_MEMORY:
Radek Krejcic5a12e12020-05-27 17:09:59 +0200333 if (out->method.mem.buf && *out->method.mem.buf) {
334 memset(*out->method.mem.buf, 0, out->method.mem.len);
335 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100336 out->printed = 0;
337 out->method.mem.len = 0;
338 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200339 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100340 /* nothing to do (not seekable) */
341 break;
342 }
343
344 return LY_SUCCESS;
345}
346
Radek Krejci84ce7b12020-06-11 17:28:25 +0200347API LY_ERR
348ly_out_new_filepath(const char *filepath, struct ly_out **out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100349{
Radek Krejci84ce7b12020-06-11 17:28:25 +0200350 LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100351
Radek Krejci84ce7b12020-06-11 17:28:25 +0200352 *out = calloc(1, sizeof **out);
353 LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
Radek Krejcia5bba312020-01-09 15:41:20 +0100354
Radek Krejci84ce7b12020-06-11 17:28:25 +0200355 (*out)->type = LY_OUT_FILEPATH;
356 (*out)->method.fpath.f = fopen(filepath, "w");
357 if (!(*out)->method.fpath.f) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100358 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
Radek Krejcif6923e82020-07-02 16:36:53 +0200359 free(*out);
360 *out = NULL;
Radek Krejci84ce7b12020-06-11 17:28:25 +0200361 return LY_ESYS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100362 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200363 (*out)->method.fpath.filepath = strdup(filepath);
364 return LY_SUCCESS;
Radek Krejcia5bba312020-01-09 15:41:20 +0100365}
366
367API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200368ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100369{
370 FILE *f;
371
Radek Krejci241f6b52020-05-21 18:13:49 +0200372 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100373
374 if (!filepath) {
375 return out->method.fpath.filepath;
376 }
377
378 /* replace filepath */
379 f = out->method.fpath.f;
380 out->method.fpath.f = fopen(filepath, "w");
381 if (!out->method.fpath.f) {
382 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
383 out->method.fpath.f = f;
384 return ((void *)-1);
385 }
386 fclose(f);
387 free(out->method.fpath.filepath);
388 out->method.fpath.filepath = strdup(filepath);
389
390 return NULL;
391}
392
393API void
Radek Krejci857189e2020-09-01 13:26:36 +0200394ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100395{
396 if (!out) {
397 return;
398 }
399
400 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200401 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100402 if (clb_arg_destructor) {
403 clb_arg_destructor(out->method.clb.arg);
404 }
405 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200406 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100407 fclose(out->method.fdstream.f);
408 if (destroy) {
409 close(out->method.fdstream.fd);
410 }
411 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200412 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100413 if (destroy) {
414 close(out->method.fd);
415 }
416 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200417 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100418 if (destroy) {
419 fclose(out->method.f);
420 }
421 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200422 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100423 if (destroy) {
424 free(*out->method.mem.buf);
425 }
426 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200427 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100428 free(out->method.fpath.filepath);
Radek Krejci2aae3752020-05-27 18:16:30 +0200429 fclose(out->method.fpath.f);
Radek Krejcia5bba312020-01-09 15:41:20 +0100430 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200431 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100432 LOGINT(NULL);
433 }
434 free(out);
435}
436
Michal Vasko5233e962020-08-14 14:26:20 +0200437static LY_ERR
438ly_vprint_(struct ly_out *out, const char *format, va_list ap)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200439{
Michal Vasko5233e962020-08-14 14:26:20 +0200440 LY_ERR ret;
441 int written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200442 char *msg = NULL, *aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200443
444 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200445 case LY_OUT_FD:
Michal Vasko5233e962020-08-14 14:26:20 +0200446 written = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200447 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200448 case LY_OUT_FDSTREAM:
449 case LY_OUT_FILEPATH:
450 case LY_OUT_FILE:
Michal Vasko5233e962020-08-14 14:26:20 +0200451 written = vfprintf(out->method.f, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200452 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200453 case LY_OUT_MEMORY:
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 if (out->method.mem.len + written + 1 > out->method.mem.size) {
458 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200459 if (!aux) {
460 out->method.mem.buf = NULL;
461 out->method.mem.len = 0;
462 out->method.mem.size = 0;
463 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200464 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200465 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100466 *out->method.mem.buf = aux;
Michal Vasko5233e962020-08-14 14:26:20 +0200467 out->method.mem.size = out->method.mem.len + written + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200468 }
Michal Vasko5233e962020-08-14 14:26:20 +0200469 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
470 out->method.mem.len += written;
Radek Krejcia5bba312020-01-09 15:41:20 +0100471 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200472 free(msg);
473 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200474 case LY_OUT_CALLBACK:
Michal Vasko5233e962020-08-14 14:26:20 +0200475 if ((written = vasprintf(&msg, format, ap)) < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200476 break;
477 }
Michal Vasko5233e962020-08-14 14:26:20 +0200478 written = out->method.clb.func(out->method.clb.arg, msg, written);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200479 free(msg);
480 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200481 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100482 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200483 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200484 }
485
Michal Vasko5233e962020-08-14 14:26:20 +0200486 if (written < 0) {
487 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
488 written = 0;
489 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200490 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200491 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100492 /* move the original file descriptor to the end of the output file */
493 lseek(out->method.fdstream.fd, 0, SEEK_END);
494 }
Michal Vasko5233e962020-08-14 14:26:20 +0200495 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200496 }
Michal Vasko5233e962020-08-14 14:26:20 +0200497
498 out->printed += written;
499 out->func_printed += written;
500 return ret;
501}
502
503LY_ERR
504ly_print_(struct ly_out *out, const char *format, ...)
505{
506 LY_ERR ret;
507 va_list ap;
508
509 va_start(ap, format);
510 ret = ly_vprint_(out, format, ap);
511 va_end(ap);
512
513 return ret;
514}
515
516API LY_ERR
517ly_print(struct ly_out *out, const char *format, ...)
518{
519 LY_ERR ret;
520 va_list ap;
521
522 out->func_printed = 0;
523
524 va_start(ap, format);
525 ret = ly_vprint_(out, format, ap);
526 va_end(ap);
527
528 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200529}
530
Radek Krejci2aae3752020-05-27 18:16:30 +0200531API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200532ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200533{
534 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200535 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100536 /* move the original file descriptor to the end of the output file */
537 lseek(out->method.fdstream.fd, 0, SEEK_END);
538 fflush(out->method.fdstream.f);
539 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200540 case LY_OUT_FILEPATH:
541 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200542 fflush(out->method.f);
543 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200544 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200545 fsync(out->method.fd);
546 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200547 case LY_OUT_MEMORY:
548 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200549 /* nothing to do */
550 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200551 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100552 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200553 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200554
555 free(out->buffered);
556 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200557}
558
Michal Vasko5233e962020-08-14 14:26:20 +0200559LY_ERR
560ly_write_(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200561{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200562 LY_ERR ret = LY_SUCCESS;
563 size_t written = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200564
565 if (out->hole_count) {
566 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200567 if (out->buf_len + len > out->buf_size) {
568 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200569 if (!out->buffered) {
570 out->buf_len = 0;
571 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200572 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200573 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200574 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200575 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200576 }
577
Radek Krejcie7b95092019-05-15 11:03:07 +0200578 memcpy(&out->buffered[out->buf_len], buf, len);
579 out->buf_len += len;
Michal Vasko5233e962020-08-14 14:26:20 +0200580
581 out->printed += len;
582 out->func_printed += len;
583 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200584 }
585
Radek Krejci897ad2e2019-04-29 16:43:07 +0200586repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200587 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200588 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200589 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100590 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
591 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200592 out->method.mem.len = 0;
593 out->method.mem.size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200594 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200595 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200596 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200597 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200598 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100599 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
Radek Krejcie7b95092019-05-15 11:03:07 +0200600 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100601 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200602
Michal Vasko5233e962020-08-14 14:26:20 +0200603 written = len;
604 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200605 case LY_OUT_FD: {
606 ssize_t r;
607 r = write(out->method.fd, buf, len);
608 if (r < 0) {
609 ret = LY_ESYS;
610 } else {
611 written = (size_t)r;
612 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200613 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200614 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200615 case LY_OUT_FDSTREAM:
616 case LY_OUT_FILEPATH:
617 case LY_OUT_FILE:
Michal Vasko63f3d842020-07-08 10:10:14 +0200618 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200619 if (written != len) {
620 ret = LY_ESYS;
621 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200622 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200623 case LY_OUT_CALLBACK: {
624 ssize_t r;
625 r = out->method.clb.func(out->method.clb.arg, buf, len);
626 if (r < 0) {
627 ret = LY_ESYS;
628 } else {
629 written = (size_t)r;
630 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200631 break;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200632 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200633 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100634 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200635 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200636 }
637
Radek Krejci1deb5be2020-08-26 16:43:36 +0200638 if (ret) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200639 if (errno == EAGAIN || errno == EWOULDBLOCK) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200640 ret = LY_SUCCESS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200641 goto repeat;
642 }
Michal Vasko5233e962020-08-14 14:26:20 +0200643 LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
644 written = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200645 } else if ((size_t)written != len) {
Michal Vasko5233e962020-08-14 14:26:20 +0200646 LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
Michal Vasko63f3d842020-07-08 10:10:14 +0200647 len - (size_t)written, len);
Michal Vasko5233e962020-08-14 14:26:20 +0200648 ret = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200649 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200650 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100651 /* move the original file descriptor to the end of the output file */
652 lseek(out->method.fdstream.fd, 0, SEEK_END);
653 }
Michal Vasko5233e962020-08-14 14:26:20 +0200654 ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200655 }
Michal Vasko5233e962020-08-14 14:26:20 +0200656
657 out->printed += written;
658 out->func_printed += written;
659 return ret;
660}
661
662API LY_ERR
663ly_write(struct ly_out *out, const char *buf, size_t len)
664{
665 out->func_printed = 0;
666
667 return ly_write_(out, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200668}
669
Michal Vasko63f3d842020-07-08 10:10:14 +0200670API size_t
671ly_out_printed(const struct ly_out *out)
672{
673 return out->func_printed;
674}
675
Michal Vasko5233e962020-08-14 14:26:20 +0200676LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200677ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200678{
679 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200680 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200681 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100682 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
683 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200684 out->method.mem.len = 0;
685 out->method.mem.size = 0;
Michal Vasko5233e962020-08-14 14:26:20 +0200686 LOGMEM(NULL);
687 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200688 }
689 out->method.mem.size = out->method.mem.len + count;
690 }
691
692 /* save the current position */
693 *position = out->method.mem.len;
694
695 /* skip the memory */
696 out->method.mem.len += count;
697 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200698 case LY_OUT_FD:
699 case LY_OUT_FDSTREAM:
700 case LY_OUT_FILEPATH:
701 case LY_OUT_FILE:
702 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200703 /* buffer the hole */
704 if (out->buf_len + count > out->buf_size) {
705 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
706 if (!out->buffered) {
707 out->buf_len = 0;
708 out->buf_size = 0;
Radek Krejcibaeb8382020-05-27 16:44:53 +0200709 LOGMEM(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200710 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200711 }
712 out->buf_size = out->buf_len + count;
713 }
714
715 /* save the current position */
716 *position = out->buf_len;
717
718 /* skip the memory */
719 out->buf_len += count;
720
721 /* increase hole counter */
722 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100723 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200724 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100725 LOGINT(NULL);
Michal Vasko5233e962020-08-14 14:26:20 +0200726 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200727 }
728
Michal Vasko5233e962020-08-14 14:26:20 +0200729 /* update printed bytes counter despite we actually printed just a hole */
730 out->printed += count;
731 out->func_printed += count;
732 return LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200733}
734
Michal Vasko66d99972020-06-29 13:37:42 +0200735LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200736ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200737{
Michal Vasko66d99972020-06-29 13:37:42 +0200738 LY_ERR ret = LY_SUCCESS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200739
740 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200741 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200742 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100743 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200744 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200745 case LY_OUT_FD:
746 case LY_OUT_FDSTREAM:
747 case LY_OUT_FILEPATH:
748 case LY_OUT_FILE:
749 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200750 if (out->buf_len < position + count) {
Michal Vasko5233e962020-08-14 14:26:20 +0200751 LOGMEM(NULL);
752 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200753 }
754
755 /* write into the hole */
756 memcpy(&out->buffered[position], buf, count);
757
758 /* decrease hole counter */
759 --out->hole_count;
760
761 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200762 /* all holes filled, we can write the buffer,
Michal Vasko5233e962020-08-14 14:26:20 +0200763 * printed bytes counter is updated by ly_write_() */
764 ret = ly_write_(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200765 out->buf_len = 0;
766 }
767 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200768 case LY_OUT_ERROR:
Michal Vasko5233e962020-08-14 14:26:20 +0200769 LOGINT(NULL);
770 return LY_EINT;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200771 }
772
Radek Krejci241f6b52020-05-21 18:13:49 +0200773 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100774 /* move the original file descriptor to the end of the output file */
775 lseek(out->method.fdstream.fd, 0, SEEK_END);
776 }
Michal Vasko66d99972020-06-29 13:37:42 +0200777 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200778}