blob: 1cf666fbb76864d017cf9793d7622d34c1cdbb42 [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) {
132 if (elem->schema->flags & LYS_CONFIG_R) {
133 return 1;
134 }
135 LYD_TREE_DFS_END(node, next, elem)
136 }
137 return 0;
138 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYDP_KEEPEMPTYCONT)) {
139 /* avoid empty default containers */
140 LYD_TREE_DFS_BEGIN(node, next, elem) {
141 if (elem->schema->nodetype != LYS_CONTAINER) {
142 return 1;
143 }
144 assert(elem->flags & LYD_DEFAULT);
145 LYD_TREE_DFS_END(node, next, elem)
146 }
147 return 0;
148 }
149
150 return 1;
151}
152
Radek Krejci241f6b52020-05-21 18:13:49 +0200153API LY_OUT_TYPE
154ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100155{
Radek Krejci241f6b52020-05-21 18:13:49 +0200156 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +0100157 return out->type;
158}
159
Radek Krejci241f6b52020-05-21 18:13:49 +0200160API struct ly_out *
161ly_out_new_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100162{
Radek Krejci241f6b52020-05-21 18:13:49 +0200163 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100164
165 out = calloc(1, sizeof *out);
166 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
167
Radek Krejci241f6b52020-05-21 18:13:49 +0200168 out->type = LY_OUT_CALLBACK;
Radek Krejcia5bba312020-01-09 15:41:20 +0100169 out->method.clb.func = writeclb;
170 out->method.clb.arg = arg;
171
172 return out;
173}
174
Radek Krejci241f6b52020-05-21 18:13:49 +0200175API 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 +0100176{
177 void *prev_clb;
178
Radek Krejci241f6b52020-05-21 18:13:49 +0200179 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100180
181 prev_clb = out->method.clb.func;
182
183 if (writeclb) {
184 out->method.clb.func = writeclb;
185 }
186
187 return prev_clb;
188}
189
190API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200191ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100192{
193 void *prev_arg;
194
Radek Krejci241f6b52020-05-21 18:13:49 +0200195 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100196
197 prev_arg = out->method.clb.arg;
198
199 if (arg) {
200 out->method.clb.arg = arg;
201 }
202
203 return prev_arg;
204}
205
Radek Krejci241f6b52020-05-21 18:13:49 +0200206API struct ly_out *
207ly_out_new_fd(int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100208{
Radek Krejci241f6b52020-05-21 18:13:49 +0200209 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100210
211 out = calloc(1, sizeof *out);
212 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
213
214#ifdef HAVE_VDPRINTF
Radek Krejci241f6b52020-05-21 18:13:49 +0200215 out->type = LY_OUT_FD;
Radek Krejcia5bba312020-01-09 15:41:20 +0100216 out->method.fd = fd;
217#else
218 /* Without vdfprintf(), change the printing method to printing to a FILE stream.
219 * To preserve the original file descriptor, duplicate it and use it to open file stream. */
Radek Krejci241f6b52020-05-21 18:13:49 +0200220 out->type = LY_OUT_FDSTREAM;
Radek Krejcia5bba312020-01-09 15:41:20 +0100221 out->method.fdstream.fd = fd;
222
223 fd = dup(out->method.fdstream.fd);
224 if (fd < 0) {
225 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
226 out->method.fdstream.fd, strerror(errno));
227 free(out);
228 return NULL;
229 }
230 out->method.fdstream.f = fdopen(fd, "a");
231 if (!out->method.fdstream.f) {
232 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
233 out->method.fdstream.fd, strerror(errno));
234 free(out);
235 fclose(fd);
236 return NULL;
237 }
238#endif
239
240 return out;
241}
242
243API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200244ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100245{
246 int prev_fd;
247
Radek Krejci241f6b52020-05-21 18:13:49 +0200248 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100249
Radek Krejci241f6b52020-05-21 18:13:49 +0200250 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100251 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200252 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100253 prev_fd = out->method.fd;
254 }
255
256 if (fd != -1) {
257 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200258 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100259 int streamfd;
260 FILE *stream;
261
262 streamfd = dup(fd);
263 if (streamfd < 0) {
264 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
265 return -1;
266 }
267 stream = fdopen(streamfd, "a");
268 if (!stream) {
269 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
270 close(streamfd);
271 return -1;
272 }
273 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
274 fclose(out->method.fdstream.f);
275 out->method.fdstream.f = stream;
276 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200277 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100278 out->method.fd = fd;
279 }
280 }
281
282 return prev_fd;
283}
284
Radek Krejci241f6b52020-05-21 18:13:49 +0200285API struct ly_out *
286ly_out_new_file(FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100287{
Radek Krejci241f6b52020-05-21 18:13:49 +0200288 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100289
290 out = calloc(1, sizeof *out);
291 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
292
Radek Krejci241f6b52020-05-21 18:13:49 +0200293 out->type = LY_OUT_FILE;
Radek Krejcia5bba312020-01-09 15:41:20 +0100294 out->method.f = f;
295
296 return out;
297}
298
299API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200300ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100301{
302 FILE *prev_f;
303
Radek Krejci241f6b52020-05-21 18:13:49 +0200304 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100305
306 prev_f = out->method.f;
307
308 if (f) {
309 out->method.f = f;
310 }
311
312 return prev_f;
313}
314
Radek Krejci241f6b52020-05-21 18:13:49 +0200315API struct ly_out *
316ly_out_new_memory(char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100317{
Radek Krejci241f6b52020-05-21 18:13:49 +0200318 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100319
320 out = calloc(1, sizeof *out);
321 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
322
Radek Krejci241f6b52020-05-21 18:13:49 +0200323 out->type = LY_OUT_MEMORY;
Radek Krejcia5bba312020-01-09 15:41:20 +0100324 out->method.mem.buf = strp;
325 if (!size) {
326 /* buffer is supposed to be allocated */
327 *strp = NULL;
328 } else if (*strp) {
329 /* there is already buffer to use */
330 out->method.mem.size = size;
331 }
332
333 return out;
334}
335
336char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200337ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100338{
339 char *data;
340
Radek Krejci241f6b52020-05-21 18:13:49 +0200341 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100342
343 data = *out->method.mem.buf;
344
345 if (strp) {
346 out->method.mem.buf = strp;
347 out->method.mem.len = out->method.mem.size = 0;
348 out->printed = 0;
349 if (!size) {
350 /* buffer is supposed to be allocated */
351 *strp = NULL;
352 } else if (*strp) {
353 /* there is already buffer to use */
354 out->method.mem.size = size;
355 }
356 }
357
358 return data;
359}
360
361API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200362ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100363{
364 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
365
366 switch(out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200367 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100368 LOGINT(NULL);
369 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200370 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100371 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && errno != ESPIPE) {
372 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
373 return LY_ESYS;
374 }
375 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200376 case LY_OUT_FDSTREAM:
377 case LY_OUT_FILE:
378 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100379 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && errno != ESPIPE) {
380 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
381 return LY_ESYS;
382 }
383 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200384 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100385 out->printed = 0;
386 out->method.mem.len = 0;
387 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200388 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100389 /* nothing to do (not seekable) */
390 break;
391 }
392
393 return LY_SUCCESS;
394}
395
Radek Krejci241f6b52020-05-21 18:13:49 +0200396API struct ly_out *
397ly_out_new_filepath(const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100398{
Radek Krejci241f6b52020-05-21 18:13:49 +0200399 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100400
401 out = calloc(1, sizeof *out);
402 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
403
Radek Krejci241f6b52020-05-21 18:13:49 +0200404 out->type = LY_OUT_FILEPATH;
Radek Krejcia5bba312020-01-09 15:41:20 +0100405 out->method.fpath.f = fopen(filepath, "w");
406 if (!out->method.fpath.f) {
407 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
408 return NULL;
409 }
410 out->method.fpath.filepath = strdup(filepath);
411 return out;
412}
413
414API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200415ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100416{
417 FILE *f;
418
Radek Krejci241f6b52020-05-21 18:13:49 +0200419 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100420
421 if (!filepath) {
422 return out->method.fpath.filepath;
423 }
424
425 /* replace filepath */
426 f = out->method.fpath.f;
427 out->method.fpath.f = fopen(filepath, "w");
428 if (!out->method.fpath.f) {
429 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
430 out->method.fpath.f = f;
431 return ((void *)-1);
432 }
433 fclose(f);
434 free(out->method.fpath.filepath);
435 out->method.fpath.filepath = strdup(filepath);
436
437 return NULL;
438}
439
440API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200441ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), int destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100442{
443 if (!out) {
444 return;
445 }
446
447 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200448 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100449 if (clb_arg_destructor) {
450 clb_arg_destructor(out->method.clb.arg);
451 }
452 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200453 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100454 fclose(out->method.fdstream.f);
455 if (destroy) {
456 close(out->method.fdstream.fd);
457 }
458 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200459 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100460 if (destroy) {
461 close(out->method.fd);
462 }
463 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200464 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100465 if (destroy) {
466 fclose(out->method.f);
467 }
468 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200469 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100470 if (destroy) {
471 free(*out->method.mem.buf);
472 }
473 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200474 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100475 free(out->method.fpath.filepath);
476 if (destroy) {
477 fclose(out->method.fpath.f);
478 }
479 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200480 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100481 LOGINT(NULL);
482 }
483 free(out);
484}
485
486API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200487ly_print(struct ly_out *out, const char *format, ...)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200488{
489 int count = 0;
490 char *msg = NULL, *aux;
491 va_list ap;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200492
Radek Krejci56cc0872019-04-30 09:22:27 +0200493 LYOUT_CHECK(out, out->status);
494
Radek Krejcid3ca0632019-04-16 16:54:54 +0200495 va_start(ap, format);
496
497 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200498 case LY_OUT_FD:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200499#ifdef HAVE_VDPRINTF
500 count = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200501 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200502#else
Radek Krejci241f6b52020-05-21 18:13:49 +0200503 /* 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 +0100504 LOGINT(NULL);
505 return LY_EINT;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200506#endif
Radek Krejci241f6b52020-05-21 18:13:49 +0200507 case LY_OUT_FDSTREAM:
508 case LY_OUT_FILEPATH:
509 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200510 count = vfprintf(out->method.f, format, ap);
511 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200512 case LY_OUT_MEMORY:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200513 if ((count = vasprintf(&msg, format, ap)) < 0) {
514 break;
515 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200516 if (out->method.mem.len + count + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100517 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + count + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200518 if (!aux) {
519 out->method.mem.buf = NULL;
520 out->method.mem.len = 0;
521 out->method.mem.size = 0;
522 LOGMEM(NULL);
523 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200524 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200525 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100526 *out->method.mem.buf = aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200527 out->method.mem.size = out->method.mem.len + count + 1;
528 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100529 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200530 out->method.mem.len += count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100531 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200532 free(msg);
533 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200534 case LY_OUT_CALLBACK:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200535 if ((count = vasprintf(&msg, format, ap)) < 0) {
536 break;
537 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100538 count = out->method.clb.func(out->method.clb.arg, msg, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200539 free(msg);
540 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200541 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100542 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200543 }
544
545 va_end(ap);
546
547 if (count < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200548 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200549 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200550 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200551 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200552 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100553 /* move the original file descriptor to the end of the output file */
554 lseek(out->method.fdstream.fd, 0, SEEK_END);
555 }
Radek Krejci897ad2e2019-04-29 16:43:07 +0200556 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200557 return LY_SUCCESS;
558 }
559}
560
561void
Radek Krejci241f6b52020-05-21 18:13:49 +0200562ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200563{
564 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200565 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100566 /* move the original file descriptor to the end of the output file */
567 lseek(out->method.fdstream.fd, 0, SEEK_END);
568 fflush(out->method.fdstream.f);
569 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200570 case LY_OUT_FILEPATH:
571 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200572 fflush(out->method.f);
573 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200574 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200575 fsync(out->method.fd);
576 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200577 case LY_OUT_MEMORY:
578 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200579 /* nothing to do */
580 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200581 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100582 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200583 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200584
585 free(out->buffered);
586 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200587}
588
Radek Krejcia5bba312020-01-09 15:41:20 +0100589API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200590ly_write(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200591{
592 int written = 0;
593
Radek Krejci56cc0872019-04-30 09:22:27 +0200594 LYOUT_CHECK(out, out->status);
595
Radek Krejcid3ca0632019-04-16 16:54:54 +0200596 if (out->hole_count) {
597 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200598 if (out->buf_len + len > out->buf_size) {
599 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200600 if (!out->buffered) {
601 out->buf_len = 0;
602 out->buf_size = 0;
603 LOGMEM_RET(NULL);
604 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200605 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200606 }
607
Radek Krejcie7b95092019-05-15 11:03:07 +0200608 memcpy(&out->buffered[out->buf_len], buf, len);
609 out->buf_len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200610 return LY_SUCCESS;
611 }
612
Radek Krejci897ad2e2019-04-29 16:43:07 +0200613repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200614 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200615 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200616 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100617 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
618 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200619 out->method.mem.len = 0;
620 out->method.mem.size = 0;
621 LOGMEM_RET(NULL);
622 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200623 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200624 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100625 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
Radek Krejcie7b95092019-05-15 11:03:07 +0200626 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100627 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200628
Radek Krejcie7b95092019-05-15 11:03:07 +0200629 out->printed += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200630 return LY_SUCCESS;
Radek Krejci241f6b52020-05-21 18:13:49 +0200631 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200632 written = write(out->method.fd, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200633 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200634 case LY_OUT_FDSTREAM:
635 case LY_OUT_FILEPATH:
636 case LY_OUT_FILE:
Radek Krejcie7b95092019-05-15 11:03:07 +0200637 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200638 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200639 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100640 written = out->method.clb.func(out->method.clb.arg, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200641 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200642 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100643 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200644 }
645
646 if (written < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200647 if (errno == EAGAIN || errno == EWOULDBLOCK) {
648 goto repeat;
649 }
650 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200651 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200652 return LY_ESYS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200653 } else if ((size_t)written != len) {
654 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 +0200655 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200656 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200657 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200658 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100659 /* move the original file descriptor to the end of the output file */
660 lseek(out->method.fdstream.fd, 0, SEEK_END);
661 }
Radek Krejci897ad2e2019-04-29 16:43:07 +0200662 out->printed += written;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200663 return LY_SUCCESS;
664 }
665}
666
667LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200668ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200669{
Radek Krejci56cc0872019-04-30 09:22:27 +0200670 LYOUT_CHECK(out, out->status);
671
Radek Krejcid3ca0632019-04-16 16:54:54 +0200672 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200673 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200674 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100675 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
676 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200677 out->method.mem.len = 0;
678 out->method.mem.size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200679 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200680 LOGMEM_RET(NULL);
681 }
682 out->method.mem.size = out->method.mem.len + count;
683 }
684
685 /* save the current position */
686 *position = out->method.mem.len;
687
688 /* skip the memory */
689 out->method.mem.len += count;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200690
691 /* update printed bytes counter despite we actually printed just a hole */
692 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200693 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200694 case LY_OUT_FD:
695 case LY_OUT_FDSTREAM:
696 case LY_OUT_FILEPATH:
697 case LY_OUT_FILE:
698 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200699 /* buffer the hole */
700 if (out->buf_len + count > out->buf_size) {
701 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
702 if (!out->buffered) {
703 out->buf_len = 0;
704 out->buf_size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200705 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200706 LOGMEM_RET(NULL);
707 }
708 out->buf_size = out->buf_len + count;
709 }
710
711 /* save the current position */
712 *position = out->buf_len;
713
714 /* skip the memory */
715 out->buf_len += count;
716
717 /* increase hole counter */
718 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100719
720 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200721 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100722 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200723 }
724
725 return LY_SUCCESS;
726}
727
728LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200729ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200730{
731 LY_ERR ret = LY_SUCCESS;
732
Radek Krejci56cc0872019-04-30 09:22:27 +0200733 LYOUT_CHECK(out, out->status);
734
Radek Krejcid3ca0632019-04-16 16:54:54 +0200735 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200736 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200737 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100738 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200739 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200740 case LY_OUT_FD:
741 case LY_OUT_FDSTREAM:
742 case LY_OUT_FILEPATH:
743 case LY_OUT_FILE:
744 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200745 if (out->buf_len < position + count) {
Radek Krejci56cc0872019-04-30 09:22:27 +0200746 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200747 LOGMEM_RET(NULL);
748 }
749
750 /* write into the hole */
751 memcpy(&out->buffered[position], buf, count);
752
753 /* decrease hole counter */
754 --out->hole_count;
755
756 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200757 /* all holes filled, we can write the buffer,
758 * printed bytes counter is updated by ly_write() */
Radek Krejci241f6b52020-05-21 18:13:49 +0200759 ret = ly_write(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200760 out->buf_len = 0;
761 }
762 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200763 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100764 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200765 }
766
Radek Krejci241f6b52020-05-21 18:13:49 +0200767 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100768 /* move the original file descriptor to the end of the output file */
769 lseek(out->method.fdstream.fd, 0, SEEK_END);
770 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200771 return ret;
772}