blob: 137fe69de16bac73a6386276ff504cfce7878029 [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"
28#include "config.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[] = {
40 {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 */
72};
73
Michal Vasko6f4cbb62020-02-28 11:15:47 +010074int
Michal Vasko9b368d32020-02-14 13:53:31 +010075ly_is_default(const struct lyd_node *node)
76{
77 const struct lysc_node_leaf *leaf;
78 const struct lysc_node_leaflist *llist;
79 const struct lyd_node_term *term;
Radek Krejci7eb54ba2020-05-18 16:30:04 +020080 LY_ARRAY_SIZE_TYPE u;
Michal Vasko9b368d32020-02-14 13:53:31 +010081
82 assert(node->schema->nodetype & LYD_NODE_TERM);
83 term = (const struct lyd_node_term *)node;
84
85 if (node->schema->nodetype == LYS_LEAF) {
86 leaf = (const struct lysc_node_leaf *)node->schema;
87 if (!leaf->dflt) {
88 return 0;
89 }
90
91 /* compare with the default value */
92 if (leaf->type->plugin->compare(&term->value, leaf->dflt)) {
93 return 0;
94 }
95 } else {
96 llist = (const struct lysc_node_leaflist *)node->schema;
97 if (!llist->dflts) {
98 return 0;
99 }
100
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200101 LY_ARRAY_FOR(llist->dflts, u) {
Michal Vasko9b368d32020-02-14 13:53:31 +0100102 /* compare with each possible default value */
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200103 if (llist->type->plugin->compare(&term->value, llist->dflts[u])) {
Michal Vasko9b368d32020-02-14 13:53:31 +0100104 return 0;
105 }
106 }
107 }
108
109 return 1;
110}
111
112int
113ly_should_print(const struct lyd_node *node, int options)
114{
115 const struct lyd_node *next, *elem;
116
117 if (options & LYDP_WD_TRIM) {
118 /* do not print default nodes */
119 if (node->flags & LYD_DEFAULT) {
120 /* implicit default node/NP container with only default nodes */
121 return 0;
122 } else if (node->schema->nodetype & LYD_NODE_TERM) {
123 if (ly_is_default(node)) {
124 /* explicit default node */
125 return 0;
126 }
127 }
128 } else if ((node->flags & LYD_DEFAULT) && !(options & LYDP_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200129 /* LYDP_WD_EXPLICIT
Michal Vasko9b368d32020-02-14 13:53:31 +0100130 * - print only if it contains status data in its subtree */
131 LYD_TREE_DFS_BEGIN(node, next, elem) {
Michal Vaskodb4f9e42020-06-01 17:29:56 +0200132 if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
133 if (elem->schema->flags & LYS_CONFIG_R) {
134 return 1;
135 }
Michal Vasko9b368d32020-02-14 13:53:31 +0100136 }
137 LYD_TREE_DFS_END(node, next, elem)
138 }
139 return 0;
140 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYDP_KEEPEMPTYCONT)) {
141 /* avoid empty default containers */
142 LYD_TREE_DFS_BEGIN(node, next, elem) {
143 if (elem->schema->nodetype != LYS_CONTAINER) {
144 return 1;
145 }
146 assert(elem->flags & LYD_DEFAULT);
147 LYD_TREE_DFS_END(node, next, elem)
148 }
149 return 0;
150 }
151
152 return 1;
153}
154
Radek Krejci241f6b52020-05-21 18:13:49 +0200155API LY_OUT_TYPE
156ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100157{
Radek Krejci241f6b52020-05-21 18:13:49 +0200158 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +0100159 return out->type;
160}
161
Radek Krejci241f6b52020-05-21 18:13:49 +0200162API struct ly_out *
163ly_out_new_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100164{
Radek Krejci241f6b52020-05-21 18:13:49 +0200165 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100166
167 out = calloc(1, sizeof *out);
168 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
169
Radek Krejci241f6b52020-05-21 18:13:49 +0200170 out->type = LY_OUT_CALLBACK;
Radek Krejcia5bba312020-01-09 15:41:20 +0100171 out->method.clb.func = writeclb;
172 out->method.clb.arg = arg;
173
174 return out;
175}
176
Radek Krejci241f6b52020-05-21 18:13:49 +0200177API ssize_t (*ly_out_clb(struct ly_out *out, ssize_t (*writeclb)(void *arg, const void *buf, size_t count)))(void *arg, const void *buf, size_t count)
Radek Krejcia5bba312020-01-09 15:41:20 +0100178{
179 void *prev_clb;
180
Radek Krejci241f6b52020-05-21 18:13:49 +0200181 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100182
183 prev_clb = out->method.clb.func;
184
185 if (writeclb) {
186 out->method.clb.func = writeclb;
187 }
188
189 return prev_clb;
190}
191
192API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200193ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100194{
195 void *prev_arg;
196
Radek Krejci241f6b52020-05-21 18:13:49 +0200197 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100198
199 prev_arg = out->method.clb.arg;
200
201 if (arg) {
202 out->method.clb.arg = arg;
203 }
204
205 return prev_arg;
206}
207
Radek Krejci241f6b52020-05-21 18:13:49 +0200208API struct ly_out *
209ly_out_new_fd(int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100210{
Radek Krejci241f6b52020-05-21 18:13:49 +0200211 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100212
213 out = calloc(1, sizeof *out);
214 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
215
216#ifdef HAVE_VDPRINTF
Radek Krejci241f6b52020-05-21 18:13:49 +0200217 out->type = LY_OUT_FD;
Radek Krejcia5bba312020-01-09 15:41:20 +0100218 out->method.fd = fd;
219#else
220 /* Without vdfprintf(), change the printing method to printing to a FILE stream.
221 * To preserve the original file descriptor, duplicate it and use it to open file stream. */
Radek Krejci241f6b52020-05-21 18:13:49 +0200222 out->type = LY_OUT_FDSTREAM;
Radek Krejcia5bba312020-01-09 15:41:20 +0100223 out->method.fdstream.fd = fd;
224
225 fd = dup(out->method.fdstream.fd);
226 if (fd < 0) {
227 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
228 out->method.fdstream.fd, strerror(errno));
229 free(out);
230 return NULL;
231 }
232 out->method.fdstream.f = fdopen(fd, "a");
233 if (!out->method.fdstream.f) {
234 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
235 out->method.fdstream.fd, strerror(errno));
236 free(out);
237 fclose(fd);
238 return NULL;
239 }
240#endif
241
242 return out;
243}
244
245API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200246ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100247{
248 int prev_fd;
249
Radek Krejci241f6b52020-05-21 18:13:49 +0200250 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100251
Radek Krejci241f6b52020-05-21 18:13:49 +0200252 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100253 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200254 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100255 prev_fd = out->method.fd;
256 }
257
258 if (fd != -1) {
259 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200260 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100261 int streamfd;
262 FILE *stream;
263
264 streamfd = dup(fd);
265 if (streamfd < 0) {
266 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
267 return -1;
268 }
269 stream = fdopen(streamfd, "a");
270 if (!stream) {
271 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
272 close(streamfd);
273 return -1;
274 }
275 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
276 fclose(out->method.fdstream.f);
277 out->method.fdstream.f = stream;
278 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200279 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100280 out->method.fd = fd;
281 }
282 }
283
284 return prev_fd;
285}
286
Radek Krejci241f6b52020-05-21 18:13:49 +0200287API struct ly_out *
288ly_out_new_file(FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100289{
Radek Krejci241f6b52020-05-21 18:13:49 +0200290 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100291
292 out = calloc(1, sizeof *out);
293 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
294
Radek Krejci241f6b52020-05-21 18:13:49 +0200295 out->type = LY_OUT_FILE;
Radek Krejcia5bba312020-01-09 15:41:20 +0100296 out->method.f = f;
297
298 return out;
299}
300
301API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200302ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100303{
304 FILE *prev_f;
305
Radek Krejci241f6b52020-05-21 18:13:49 +0200306 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100307
308 prev_f = out->method.f;
309
310 if (f) {
311 out->method.f = f;
312 }
313
314 return prev_f;
315}
316
Radek Krejci241f6b52020-05-21 18:13:49 +0200317API struct ly_out *
318ly_out_new_memory(char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100319{
Radek Krejci241f6b52020-05-21 18:13:49 +0200320 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100321
322 out = calloc(1, sizeof *out);
323 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
324
Radek Krejci241f6b52020-05-21 18:13:49 +0200325 out->type = LY_OUT_MEMORY;
Radek Krejcia5bba312020-01-09 15:41:20 +0100326 out->method.mem.buf = strp;
327 if (!size) {
328 /* buffer is supposed to be allocated */
329 *strp = NULL;
330 } else if (*strp) {
331 /* there is already buffer to use */
332 out->method.mem.size = size;
333 }
334
335 return out;
336}
337
338char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200339ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100340{
341 char *data;
342
Radek Krejci241f6b52020-05-21 18:13:49 +0200343 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100344
345 data = *out->method.mem.buf;
346
347 if (strp) {
348 out->method.mem.buf = strp;
349 out->method.mem.len = out->method.mem.size = 0;
350 out->printed = 0;
351 if (!size) {
352 /* buffer is supposed to be allocated */
353 *strp = NULL;
354 } else if (*strp) {
355 /* there is already buffer to use */
356 out->method.mem.size = size;
357 }
358 }
359
360 return data;
361}
362
363API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200364ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100365{
366 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
367
368 switch(out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200369 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100370 LOGINT(NULL);
371 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200372 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100373 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && errno != ESPIPE) {
374 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
375 return LY_ESYS;
376 }
377 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200378 case LY_OUT_FDSTREAM:
379 case LY_OUT_FILE:
380 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100381 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && errno != ESPIPE) {
382 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
383 return LY_ESYS;
384 }
385 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200386 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100387 out->printed = 0;
388 out->method.mem.len = 0;
389 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200390 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100391 /* nothing to do (not seekable) */
392 break;
393 }
394
395 return LY_SUCCESS;
396}
397
Radek Krejci241f6b52020-05-21 18:13:49 +0200398API struct ly_out *
399ly_out_new_filepath(const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100400{
Radek Krejci241f6b52020-05-21 18:13:49 +0200401 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100402
403 out = calloc(1, sizeof *out);
404 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
405
Radek Krejci241f6b52020-05-21 18:13:49 +0200406 out->type = LY_OUT_FILEPATH;
Radek Krejcia5bba312020-01-09 15:41:20 +0100407 out->method.fpath.f = fopen(filepath, "w");
408 if (!out->method.fpath.f) {
409 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
410 return NULL;
411 }
412 out->method.fpath.filepath = strdup(filepath);
413 return out;
414}
415
416API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200417ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100418{
419 FILE *f;
420
Radek Krejci241f6b52020-05-21 18:13:49 +0200421 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100422
423 if (!filepath) {
424 return out->method.fpath.filepath;
425 }
426
427 /* replace filepath */
428 f = out->method.fpath.f;
429 out->method.fpath.f = fopen(filepath, "w");
430 if (!out->method.fpath.f) {
431 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
432 out->method.fpath.f = f;
433 return ((void *)-1);
434 }
435 fclose(f);
436 free(out->method.fpath.filepath);
437 out->method.fpath.filepath = strdup(filepath);
438
439 return NULL;
440}
441
442API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200443ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), int destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100444{
445 if (!out) {
446 return;
447 }
448
449 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200450 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100451 if (clb_arg_destructor) {
452 clb_arg_destructor(out->method.clb.arg);
453 }
454 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200455 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100456 fclose(out->method.fdstream.f);
457 if (destroy) {
458 close(out->method.fdstream.fd);
459 }
460 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200461 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100462 if (destroy) {
463 close(out->method.fd);
464 }
465 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200466 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100467 if (destroy) {
468 fclose(out->method.f);
469 }
470 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200471 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100472 if (destroy) {
473 free(*out->method.mem.buf);
474 }
475 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200476 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100477 free(out->method.fpath.filepath);
478 if (destroy) {
479 fclose(out->method.fpath.f);
480 }
481 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200482 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100483 LOGINT(NULL);
484 }
485 free(out);
486}
487
488API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200489ly_print(struct ly_out *out, const char *format, ...)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200490{
491 int count = 0;
492 char *msg = NULL, *aux;
493 va_list ap;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200494
Radek Krejci56cc0872019-04-30 09:22:27 +0200495 LYOUT_CHECK(out, out->status);
496
Radek Krejcid3ca0632019-04-16 16:54:54 +0200497 va_start(ap, format);
498
499 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200500 case LY_OUT_FD:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200501#ifdef HAVE_VDPRINTF
502 count = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200503 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200504#else
Radek Krejci241f6b52020-05-21 18:13:49 +0200505 /* never should be here since ly_out_fd() is supposed to set type to LY_OUT_FDSTREAM in case vdprintf() is missing */
Radek Krejcia5bba312020-01-09 15:41:20 +0100506 LOGINT(NULL);
507 return LY_EINT;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200508#endif
Radek Krejci241f6b52020-05-21 18:13:49 +0200509 case LY_OUT_FDSTREAM:
510 case LY_OUT_FILEPATH:
511 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200512 count = vfprintf(out->method.f, format, ap);
513 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200514 case LY_OUT_MEMORY:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200515 if ((count = vasprintf(&msg, format, ap)) < 0) {
516 break;
517 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200518 if (out->method.mem.len + count + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100519 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + count + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200520 if (!aux) {
521 out->method.mem.buf = NULL;
522 out->method.mem.len = 0;
523 out->method.mem.size = 0;
524 LOGMEM(NULL);
525 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200526 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200527 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100528 *out->method.mem.buf = aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200529 out->method.mem.size = out->method.mem.len + count + 1;
530 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100531 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200532 out->method.mem.len += count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100533 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200534 free(msg);
535 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200536 case LY_OUT_CALLBACK:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200537 if ((count = vasprintf(&msg, format, ap)) < 0) {
538 break;
539 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100540 count = out->method.clb.func(out->method.clb.arg, msg, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200541 free(msg);
542 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200543 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100544 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200545 }
546
547 va_end(ap);
548
549 if (count < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200550 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200551 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200552 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200553 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200554 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100555 /* move the original file descriptor to the end of the output file */
556 lseek(out->method.fdstream.fd, 0, SEEK_END);
557 }
Radek Krejci897ad2e2019-04-29 16:43:07 +0200558 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200559 return LY_SUCCESS;
560 }
561}
562
563void
Radek Krejci241f6b52020-05-21 18:13:49 +0200564ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200565{
566 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200567 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100568 /* move the original file descriptor to the end of the output file */
569 lseek(out->method.fdstream.fd, 0, SEEK_END);
570 fflush(out->method.fdstream.f);
571 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200572 case LY_OUT_FILEPATH:
573 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200574 fflush(out->method.f);
575 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200576 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200577 fsync(out->method.fd);
578 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200579 case LY_OUT_MEMORY:
580 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200581 /* nothing to do */
582 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200583 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100584 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200585 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200586
587 free(out->buffered);
588 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200589}
590
Radek Krejcia5bba312020-01-09 15:41:20 +0100591API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200592ly_write(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200593{
594 int written = 0;
595
Radek Krejci56cc0872019-04-30 09:22:27 +0200596 LYOUT_CHECK(out, out->status);
597
Radek Krejcid3ca0632019-04-16 16:54:54 +0200598 if (out->hole_count) {
599 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200600 if (out->buf_len + len > out->buf_size) {
601 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200602 if (!out->buffered) {
603 out->buf_len = 0;
604 out->buf_size = 0;
605 LOGMEM_RET(NULL);
606 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200607 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200608 }
609
Radek Krejcie7b95092019-05-15 11:03:07 +0200610 memcpy(&out->buffered[out->buf_len], buf, len);
611 out->buf_len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200612 return LY_SUCCESS;
613 }
614
Radek Krejci897ad2e2019-04-29 16:43:07 +0200615repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200616 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200617 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200618 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100619 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
620 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200621 out->method.mem.len = 0;
622 out->method.mem.size = 0;
623 LOGMEM_RET(NULL);
624 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200625 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200626 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100627 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
Radek Krejcie7b95092019-05-15 11:03:07 +0200628 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100629 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200630
Radek Krejcie7b95092019-05-15 11:03:07 +0200631 out->printed += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200632 return LY_SUCCESS;
Radek Krejci241f6b52020-05-21 18:13:49 +0200633 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200634 written = write(out->method.fd, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200635 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200636 case LY_OUT_FDSTREAM:
637 case LY_OUT_FILEPATH:
638 case LY_OUT_FILE:
Radek Krejcie7b95092019-05-15 11:03:07 +0200639 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200640 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200641 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100642 written = out->method.clb.func(out->method.clb.arg, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200643 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200644 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100645 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200646 }
647
648 if (written < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200649 if (errno == EAGAIN || errno == EWOULDBLOCK) {
650 goto repeat;
651 }
652 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200653 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200654 return LY_ESYS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200655 } else if ((size_t)written != len) {
656 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__, len - (size_t)written, len);
Radek Krejci56cc0872019-04-30 09:22:27 +0200657 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200658 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200659 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200660 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100661 /* move the original file descriptor to the end of the output file */
662 lseek(out->method.fdstream.fd, 0, SEEK_END);
663 }
Radek Krejci897ad2e2019-04-29 16:43:07 +0200664 out->printed += written;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200665 return LY_SUCCESS;
666 }
667}
668
669LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200670ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200671{
Radek Krejci56cc0872019-04-30 09:22:27 +0200672 LYOUT_CHECK(out, out->status);
673
Radek Krejcid3ca0632019-04-16 16:54:54 +0200674 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200675 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200676 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100677 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
678 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200679 out->method.mem.len = 0;
680 out->method.mem.size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200681 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200682 LOGMEM_RET(NULL);
683 }
684 out->method.mem.size = out->method.mem.len + count;
685 }
686
687 /* save the current position */
688 *position = out->method.mem.len;
689
690 /* skip the memory */
691 out->method.mem.len += count;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200692
693 /* update printed bytes counter despite we actually printed just a hole */
694 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200695 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200696 case LY_OUT_FD:
697 case LY_OUT_FDSTREAM:
698 case LY_OUT_FILEPATH:
699 case LY_OUT_FILE:
700 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200701 /* buffer the hole */
702 if (out->buf_len + count > out->buf_size) {
703 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
704 if (!out->buffered) {
705 out->buf_len = 0;
706 out->buf_size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200707 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200708 LOGMEM_RET(NULL);
709 }
710 out->buf_size = out->buf_len + count;
711 }
712
713 /* save the current position */
714 *position = out->buf_len;
715
716 /* skip the memory */
717 out->buf_len += count;
718
719 /* increase hole counter */
720 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100721
722 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200723 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100724 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200725 }
726
727 return LY_SUCCESS;
728}
729
730LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200731ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200732{
733 LY_ERR ret = LY_SUCCESS;
734
Radek Krejci56cc0872019-04-30 09:22:27 +0200735 LYOUT_CHECK(out, out->status);
736
Radek Krejcid3ca0632019-04-16 16:54:54 +0200737 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200738 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200739 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100740 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200741 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200742 case LY_OUT_FD:
743 case LY_OUT_FDSTREAM:
744 case LY_OUT_FILEPATH:
745 case LY_OUT_FILE:
746 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200747 if (out->buf_len < position + count) {
Radek Krejci56cc0872019-04-30 09:22:27 +0200748 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200749 LOGMEM_RET(NULL);
750 }
751
752 /* write into the hole */
753 memcpy(&out->buffered[position], buf, count);
754
755 /* decrease hole counter */
756 --out->hole_count;
757
758 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200759 /* all holes filled, we can write the buffer,
760 * printed bytes counter is updated by ly_write() */
Radek Krejci241f6b52020-05-21 18:13:49 +0200761 ret = ly_write(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200762 out->buf_len = 0;
763 }
764 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200765 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100766 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200767 }
768
Radek Krejci241f6b52020-05-21 18:13:49 +0200769 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100770 /* move the original file descriptor to the end of the output file */
771 lseek(out->method.fdstream.fd, 0, SEEK_END);
772 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200773 return ret;
774}