blob: 0c10bfcb7688a3f86a64108e7daae55d4305ac06 [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
15#include "common.h"
16
17#include <errno.h>
18#include <stdarg.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020019#include <stdio.h>
20#include <stdlib.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020021#include <string.h>
Radek Krejcia5bba312020-01-09 15:41:20 +010022#include <sys/types.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020023#include <unistd.h>
Michal Vasko9b368d32020-02-14 13:53:31 +010024#include <assert.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020025
Radek Krejcie7b95092019-05-15 11:03:07 +020026#include "log.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020027#include "printer_internal.h"
Michal Vasko9b368d32020-02-14 13:53:31 +010028#include "plugins_types.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020029
30/**
31 * @brief informational structure shared by printers
32 */
33struct ext_substmt_info_s ext_substmt_info[] = {
34 {NULL, NULL, 0}, /**< LYEXT_SUBSTMT_SELF */
35 {"argument", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ARGUMENT */
36 {"base", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BASE */
37 {"belongs-to", "module", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BELONGSTO */
38 {"contact", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_CONTACT */
39 {"default", "value", 0}, /**< LYEXT_SUBSTMT_DEFAULT */
40 {"description", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_DESCRIPTION */
41 {"error-app-tag", "value", 0}, /**< LYEXT_SUBSTMT_ERRTAG */
42 {"error-message", "value", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ERRMSG */
43 {"key", "value", 0}, /**< LYEXT_SUBSTMT_KEY */
44 {"namespace", "uri", 0}, /**< LYEXT_SUBSTMT_NAMESPACE */
45 {"organization", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ORGANIZATION */
46 {"path", "value", 0}, /**< LYEXT_SUBSTMT_PATH */
47 {"prefix", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_PREFIX */
48 {"presence", "value", 0}, /**< LYEXT_SUBSTMT_PRESENCE */
49 {"reference", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_REFERENCE */
50 {"revision-date", "date", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REVISIONDATE */
51 {"units", "name", 0}, /**< LYEXT_SUBSTMT_UNITS */
52 {"value", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VALUE */
53 {"yang-version", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VERSION */
54 {"modifier", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MODIFIER */
55 {"require-instance", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REQINST */
56 {"yin-element", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_YINELEM */
57 {"config", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_CONFIG */
58 {"mandatory", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MANDATORY */
59 {"ordered-by", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ORDEREDBY */
60 {"status", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_STATUS */
61 {"fraction-digits", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_DIGITS */
62 {"max-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MAX */
63 {"min-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MIN */
64 {"position", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_POSITION */
65 {"unique", "tag", 0}, /**< LYEXT_SUBSTMT_UNIQUE */
66};
67
Michal Vasko6f4cbb62020-02-28 11:15:47 +010068int
Michal Vasko9b368d32020-02-14 13:53:31 +010069ly_is_default(const struct lyd_node *node)
70{
71 const struct lysc_node_leaf *leaf;
72 const struct lysc_node_leaflist *llist;
73 const struct lyd_node_term *term;
Radek Krejci7eb54ba2020-05-18 16:30:04 +020074 LY_ARRAY_SIZE_TYPE u;
Michal Vasko9b368d32020-02-14 13:53:31 +010075
76 assert(node->schema->nodetype & LYD_NODE_TERM);
77 term = (const struct lyd_node_term *)node;
78
79 if (node->schema->nodetype == LYS_LEAF) {
80 leaf = (const struct lysc_node_leaf *)node->schema;
81 if (!leaf->dflt) {
82 return 0;
83 }
84
85 /* compare with the default value */
86 if (leaf->type->plugin->compare(&term->value, leaf->dflt)) {
87 return 0;
88 }
89 } else {
90 llist = (const struct lysc_node_leaflist *)node->schema;
91 if (!llist->dflts) {
92 return 0;
93 }
94
Radek Krejci7eb54ba2020-05-18 16:30:04 +020095 LY_ARRAY_FOR(llist->dflts, u) {
Michal Vasko9b368d32020-02-14 13:53:31 +010096 /* compare with each possible default value */
Radek Krejci7eb54ba2020-05-18 16:30:04 +020097 if (llist->type->plugin->compare(&term->value, llist->dflts[u])) {
Michal Vasko9b368d32020-02-14 13:53:31 +010098 return 0;
99 }
100 }
101 }
102
103 return 1;
104}
105
106int
107ly_should_print(const struct lyd_node *node, int options)
108{
109 const struct lyd_node *next, *elem;
110
111 if (options & LYDP_WD_TRIM) {
112 /* do not print default nodes */
113 if (node->flags & LYD_DEFAULT) {
114 /* implicit default node/NP container with only default nodes */
115 return 0;
116 } else if (node->schema->nodetype & LYD_NODE_TERM) {
117 if (ly_is_default(node)) {
118 /* explicit default node */
119 return 0;
120 }
121 }
122 } else if ((node->flags & LYD_DEFAULT) && !(options & LYDP_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200123 /* LYDP_WD_EXPLICIT
Michal Vasko9b368d32020-02-14 13:53:31 +0100124 * - print only if it contains status data in its subtree */
125 LYD_TREE_DFS_BEGIN(node, next, elem) {
126 if (elem->schema->flags & LYS_CONFIG_R) {
127 return 1;
128 }
129 LYD_TREE_DFS_END(node, next, elem)
130 }
131 return 0;
132 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYDP_KEEPEMPTYCONT)) {
133 /* avoid empty default containers */
134 LYD_TREE_DFS_BEGIN(node, next, elem) {
135 if (elem->schema->nodetype != LYS_CONTAINER) {
136 return 1;
137 }
138 assert(elem->flags & LYD_DEFAULT);
139 LYD_TREE_DFS_END(node, next, elem)
140 }
141 return 0;
142 }
143
144 return 1;
145}
146
Radek Krejci241f6b52020-05-21 18:13:49 +0200147API LY_OUT_TYPE
148ly_out_type(const struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100149{
Radek Krejci241f6b52020-05-21 18:13:49 +0200150 LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
Radek Krejcia5bba312020-01-09 15:41:20 +0100151 return out->type;
152}
153
Radek Krejci241f6b52020-05-21 18:13:49 +0200154API struct ly_out *
155ly_out_new_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100156{
Radek Krejci241f6b52020-05-21 18:13:49 +0200157 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100158
159 out = calloc(1, sizeof *out);
160 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
161
Radek Krejci241f6b52020-05-21 18:13:49 +0200162 out->type = LY_OUT_CALLBACK;
Radek Krejcia5bba312020-01-09 15:41:20 +0100163 out->method.clb.func = writeclb;
164 out->method.clb.arg = arg;
165
166 return out;
167}
168
Radek Krejci241f6b52020-05-21 18:13:49 +0200169API 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 +0100170{
171 void *prev_clb;
172
Radek Krejci241f6b52020-05-21 18:13:49 +0200173 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100174
175 prev_clb = out->method.clb.func;
176
177 if (writeclb) {
178 out->method.clb.func = writeclb;
179 }
180
181 return prev_clb;
182}
183
184API void *
Radek Krejci241f6b52020-05-21 18:13:49 +0200185ly_out_clb_arg(struct ly_out *out, void *arg)
Radek Krejcia5bba312020-01-09 15:41:20 +0100186{
187 void *prev_arg;
188
Radek Krejci241f6b52020-05-21 18:13:49 +0200189 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100190
191 prev_arg = out->method.clb.arg;
192
193 if (arg) {
194 out->method.clb.arg = arg;
195 }
196
197 return prev_arg;
198}
199
Radek Krejci241f6b52020-05-21 18:13:49 +0200200API struct ly_out *
201ly_out_new_fd(int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100202{
Radek Krejci241f6b52020-05-21 18:13:49 +0200203 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100204
205 out = calloc(1, sizeof *out);
206 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
207
208#ifdef HAVE_VDPRINTF
Radek Krejci241f6b52020-05-21 18:13:49 +0200209 out->type = LY_OUT_FD;
Radek Krejcia5bba312020-01-09 15:41:20 +0100210 out->method.fd = fd;
211#else
212 /* Without vdfprintf(), change the printing method to printing to a FILE stream.
213 * To preserve the original file descriptor, duplicate it and use it to open file stream. */
Radek Krejci241f6b52020-05-21 18:13:49 +0200214 out->type = LY_OUT_FDSTREAM;
Radek Krejcia5bba312020-01-09 15:41:20 +0100215 out->method.fdstream.fd = fd;
216
217 fd = dup(out->method.fdstream.fd);
218 if (fd < 0) {
219 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
220 out->method.fdstream.fd, strerror(errno));
221 free(out);
222 return NULL;
223 }
224 out->method.fdstream.f = fdopen(fd, "a");
225 if (!out->method.fdstream.f) {
226 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
227 out->method.fdstream.fd, strerror(errno));
228 free(out);
229 fclose(fd);
230 return NULL;
231 }
232#endif
233
234 return out;
235}
236
237API int
Radek Krejci241f6b52020-05-21 18:13:49 +0200238ly_out_fd(struct ly_out *out, int fd)
Radek Krejcia5bba312020-01-09 15:41:20 +0100239{
240 int prev_fd;
241
Radek Krejci241f6b52020-05-21 18:13:49 +0200242 LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
Radek Krejcia5bba312020-01-09 15:41:20 +0100243
Radek Krejci241f6b52020-05-21 18:13:49 +0200244 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100245 prev_fd = out->method.fdstream.fd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200246 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100247 prev_fd = out->method.fd;
248 }
249
250 if (fd != -1) {
251 /* replace output stream */
Radek Krejci241f6b52020-05-21 18:13:49 +0200252 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100253 int streamfd;
254 FILE *stream;
255
256 streamfd = dup(fd);
257 if (streamfd < 0) {
258 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
259 return -1;
260 }
261 stream = fdopen(streamfd, "a");
262 if (!stream) {
263 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
264 close(streamfd);
265 return -1;
266 }
267 /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
268 fclose(out->method.fdstream.f);
269 out->method.fdstream.f = stream;
270 out->method.fdstream.fd = streamfd;
Radek Krejci241f6b52020-05-21 18:13:49 +0200271 } else { /* LY_OUT_FD */
Radek Krejcia5bba312020-01-09 15:41:20 +0100272 out->method.fd = fd;
273 }
274 }
275
276 return prev_fd;
277}
278
Radek Krejci241f6b52020-05-21 18:13:49 +0200279API struct ly_out *
280ly_out_new_file(FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100281{
Radek Krejci241f6b52020-05-21 18:13:49 +0200282 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100283
284 out = calloc(1, sizeof *out);
285 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
286
Radek Krejci241f6b52020-05-21 18:13:49 +0200287 out->type = LY_OUT_FILE;
Radek Krejcia5bba312020-01-09 15:41:20 +0100288 out->method.f = f;
289
290 return out;
291}
292
293API FILE *
Radek Krejci241f6b52020-05-21 18:13:49 +0200294ly_out_file(struct ly_out *out, FILE *f)
Radek Krejcia5bba312020-01-09 15:41:20 +0100295{
296 FILE *prev_f;
297
Radek Krejci241f6b52020-05-21 18:13:49 +0200298 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100299
300 prev_f = out->method.f;
301
302 if (f) {
303 out->method.f = f;
304 }
305
306 return prev_f;
307}
308
Radek Krejci241f6b52020-05-21 18:13:49 +0200309API struct ly_out *
310ly_out_new_memory(char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100311{
Radek Krejci241f6b52020-05-21 18:13:49 +0200312 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100313
314 out = calloc(1, sizeof *out);
315 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
316
Radek Krejci241f6b52020-05-21 18:13:49 +0200317 out->type = LY_OUT_MEMORY;
Radek Krejcia5bba312020-01-09 15:41:20 +0100318 out->method.mem.buf = strp;
319 if (!size) {
320 /* buffer is supposed to be allocated */
321 *strp = NULL;
322 } else if (*strp) {
323 /* there is already buffer to use */
324 out->method.mem.size = size;
325 }
326
327 return out;
328}
329
330char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200331ly_out_memory(struct ly_out *out, char **strp, size_t size)
Radek Krejcia5bba312020-01-09 15:41:20 +0100332{
333 char *data;
334
Radek Krejci241f6b52020-05-21 18:13:49 +0200335 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
Radek Krejcia5bba312020-01-09 15:41:20 +0100336
337 data = *out->method.mem.buf;
338
339 if (strp) {
340 out->method.mem.buf = strp;
341 out->method.mem.len = out->method.mem.size = 0;
342 out->printed = 0;
343 if (!size) {
344 /* buffer is supposed to be allocated */
345 *strp = NULL;
346 } else if (*strp) {
347 /* there is already buffer to use */
348 out->method.mem.size = size;
349 }
350 }
351
352 return data;
353}
354
355API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200356ly_out_reset(struct ly_out *out)
Radek Krejcia5bba312020-01-09 15:41:20 +0100357{
358 LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
359
360 switch(out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200361 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100362 LOGINT(NULL);
363 return LY_EINT;
Radek Krejci241f6b52020-05-21 18:13:49 +0200364 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100365 if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && errno != ESPIPE) {
366 LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
367 return LY_ESYS;
368 }
369 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200370 case LY_OUT_FDSTREAM:
371 case LY_OUT_FILE:
372 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100373 if ((fseek(out->method.f, 0, SEEK_SET) == -1) && errno != ESPIPE) {
374 LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
375 return LY_ESYS;
376 }
377 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200378 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100379 out->printed = 0;
380 out->method.mem.len = 0;
381 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200382 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100383 /* nothing to do (not seekable) */
384 break;
385 }
386
387 return LY_SUCCESS;
388}
389
Radek Krejci241f6b52020-05-21 18:13:49 +0200390API struct ly_out *
391ly_out_new_filepath(const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100392{
Radek Krejci241f6b52020-05-21 18:13:49 +0200393 struct ly_out *out;
Radek Krejcia5bba312020-01-09 15:41:20 +0100394
395 out = calloc(1, sizeof *out);
396 LY_CHECK_ERR_RET(!out, LOGMEM(NULL), NULL);
397
Radek Krejci241f6b52020-05-21 18:13:49 +0200398 out->type = LY_OUT_FILEPATH;
Radek Krejcia5bba312020-01-09 15:41:20 +0100399 out->method.fpath.f = fopen(filepath, "w");
400 if (!out->method.fpath.f) {
401 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
402 return NULL;
403 }
404 out->method.fpath.filepath = strdup(filepath);
405 return out;
406}
407
408API const char *
Radek Krejci241f6b52020-05-21 18:13:49 +0200409ly_out_filepath(struct ly_out *out, const char *filepath)
Radek Krejcia5bba312020-01-09 15:41:20 +0100410{
411 FILE *f;
412
Radek Krejci241f6b52020-05-21 18:13:49 +0200413 LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
Radek Krejcia5bba312020-01-09 15:41:20 +0100414
415 if (!filepath) {
416 return out->method.fpath.filepath;
417 }
418
419 /* replace filepath */
420 f = out->method.fpath.f;
421 out->method.fpath.f = fopen(filepath, "w");
422 if (!out->method.fpath.f) {
423 LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
424 out->method.fpath.f = f;
425 return ((void *)-1);
426 }
427 fclose(f);
428 free(out->method.fpath.filepath);
429 out->method.fpath.filepath = strdup(filepath);
430
431 return NULL;
432}
433
434API void
Radek Krejci241f6b52020-05-21 18:13:49 +0200435ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), int destroy)
Radek Krejcia5bba312020-01-09 15:41:20 +0100436{
437 if (!out) {
438 return;
439 }
440
441 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200442 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100443 if (clb_arg_destructor) {
444 clb_arg_destructor(out->method.clb.arg);
445 }
446 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200447 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100448 fclose(out->method.fdstream.f);
449 if (destroy) {
450 close(out->method.fdstream.fd);
451 }
452 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200453 case LY_OUT_FD:
Radek Krejcia5bba312020-01-09 15:41:20 +0100454 if (destroy) {
455 close(out->method.fd);
456 }
457 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200458 case LY_OUT_FILE:
Radek Krejcia5bba312020-01-09 15:41:20 +0100459 if (destroy) {
460 fclose(out->method.f);
461 }
462 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200463 case LY_OUT_MEMORY:
Radek Krejcia5bba312020-01-09 15:41:20 +0100464 if (destroy) {
465 free(*out->method.mem.buf);
466 }
467 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200468 case LY_OUT_FILEPATH:
Radek Krejcia5bba312020-01-09 15:41:20 +0100469 free(out->method.fpath.filepath);
470 if (destroy) {
471 fclose(out->method.fpath.f);
472 }
473 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200474 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100475 LOGINT(NULL);
476 }
477 free(out);
478}
479
480API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200481ly_print(struct ly_out *out, const char *format, ...)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200482{
483 int count = 0;
484 char *msg = NULL, *aux;
485 va_list ap;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200486
Radek Krejci56cc0872019-04-30 09:22:27 +0200487 LYOUT_CHECK(out, out->status);
488
Radek Krejcid3ca0632019-04-16 16:54:54 +0200489 va_start(ap, format);
490
491 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200492 case LY_OUT_FD:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200493#ifdef HAVE_VDPRINTF
494 count = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200495 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200496#else
Radek Krejci241f6b52020-05-21 18:13:49 +0200497 /* 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 +0100498 LOGINT(NULL);
499 return LY_EINT;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200500#endif
Radek Krejci241f6b52020-05-21 18:13:49 +0200501 case LY_OUT_FDSTREAM:
502 case LY_OUT_FILEPATH:
503 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200504 count = vfprintf(out->method.f, format, ap);
505 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200506 case LY_OUT_MEMORY:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200507 if ((count = vasprintf(&msg, format, ap)) < 0) {
508 break;
509 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200510 if (out->method.mem.len + count + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100511 aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + count + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200512 if (!aux) {
513 out->method.mem.buf = NULL;
514 out->method.mem.len = 0;
515 out->method.mem.size = 0;
516 LOGMEM(NULL);
517 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200518 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200519 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100520 *out->method.mem.buf = aux;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200521 out->method.mem.size = out->method.mem.len + count + 1;
522 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100523 memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200524 out->method.mem.len += count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100525 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejcid3ca0632019-04-16 16:54:54 +0200526 free(msg);
527 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200528 case LY_OUT_CALLBACK:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200529 if ((count = vasprintf(&msg, format, ap)) < 0) {
530 break;
531 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100532 count = out->method.clb.func(out->method.clb.arg, msg, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200533 free(msg);
534 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200535 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100536 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200537 }
538
539 va_end(ap);
540
541 if (count < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200542 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200543 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200544 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200545 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200546 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100547 /* move the original file descriptor to the end of the output file */
548 lseek(out->method.fdstream.fd, 0, SEEK_END);
549 }
Radek Krejci897ad2e2019-04-29 16:43:07 +0200550 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200551 return LY_SUCCESS;
552 }
553}
554
555void
Radek Krejci241f6b52020-05-21 18:13:49 +0200556ly_print_flush(struct ly_out *out)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200557{
558 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200559 case LY_OUT_FDSTREAM:
Radek Krejcia5bba312020-01-09 15:41:20 +0100560 /* move the original file descriptor to the end of the output file */
561 lseek(out->method.fdstream.fd, 0, SEEK_END);
562 fflush(out->method.fdstream.f);
563 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200564 case LY_OUT_FILEPATH:
565 case LY_OUT_FILE:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200566 fflush(out->method.f);
567 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200568 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200569 fsync(out->method.fd);
570 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200571 case LY_OUT_MEMORY:
572 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200573 /* nothing to do */
574 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200575 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100576 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200577 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200578
579 free(out->buffered);
580 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200581}
582
Radek Krejcia5bba312020-01-09 15:41:20 +0100583API LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200584ly_write(struct ly_out *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200585{
586 int written = 0;
587
Radek Krejci56cc0872019-04-30 09:22:27 +0200588 LYOUT_CHECK(out, out->status);
589
Radek Krejcid3ca0632019-04-16 16:54:54 +0200590 if (out->hole_count) {
591 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200592 if (out->buf_len + len > out->buf_size) {
593 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200594 if (!out->buffered) {
595 out->buf_len = 0;
596 out->buf_size = 0;
597 LOGMEM_RET(NULL);
598 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200599 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200600 }
601
Radek Krejcie7b95092019-05-15 11:03:07 +0200602 memcpy(&out->buffered[out->buf_len], buf, len);
603 out->buf_len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200604 return LY_SUCCESS;
605 }
606
Radek Krejci897ad2e2019-04-29 16:43:07 +0200607repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200608 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200609 case LY_OUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200610 if (out->method.mem.len + len + 1 > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100611 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
612 if (!*out->method.mem.buf) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200613 out->method.mem.len = 0;
614 out->method.mem.size = 0;
615 LOGMEM_RET(NULL);
616 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200617 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200618 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100619 memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
Radek Krejcie7b95092019-05-15 11:03:07 +0200620 out->method.mem.len += len;
Radek Krejcia5bba312020-01-09 15:41:20 +0100621 (*out->method.mem.buf)[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200622
Radek Krejcie7b95092019-05-15 11:03:07 +0200623 out->printed += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200624 return LY_SUCCESS;
Radek Krejci241f6b52020-05-21 18:13:49 +0200625 case LY_OUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200626 written = write(out->method.fd, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200627 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200628 case LY_OUT_FDSTREAM:
629 case LY_OUT_FILEPATH:
630 case LY_OUT_FILE:
Radek Krejcie7b95092019-05-15 11:03:07 +0200631 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200632 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200633 case LY_OUT_CALLBACK:
Radek Krejcia5bba312020-01-09 15:41:20 +0100634 written = out->method.clb.func(out->method.clb.arg, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200635 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200636 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100637 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200638 }
639
640 if (written < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200641 if (errno == EAGAIN || errno == EWOULDBLOCK) {
642 goto repeat;
643 }
644 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200645 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200646 return LY_ESYS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200647 } else if ((size_t)written != len) {
648 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 +0200649 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200650 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200651 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200652 if (out->type == LY_OUT_FDSTREAM) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100653 /* move the original file descriptor to the end of the output file */
654 lseek(out->method.fdstream.fd, 0, SEEK_END);
655 }
Radek Krejci897ad2e2019-04-29 16:43:07 +0200656 out->printed += written;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200657 return LY_SUCCESS;
658 }
659}
660
661LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200662ly_write_skip(struct ly_out *out, size_t count, size_t *position)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200663{
Radek Krejci56cc0872019-04-30 09:22:27 +0200664 LYOUT_CHECK(out, out->status);
665
Radek Krejcid3ca0632019-04-16 16:54:54 +0200666 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200667 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200668 if (out->method.mem.len + count > out->method.mem.size) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100669 *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
670 if (!(*out->method.mem.buf)) {
Radek Krejcid3ca0632019-04-16 16:54:54 +0200671 out->method.mem.len = 0;
672 out->method.mem.size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200673 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200674 LOGMEM_RET(NULL);
675 }
676 out->method.mem.size = out->method.mem.len + count;
677 }
678
679 /* save the current position */
680 *position = out->method.mem.len;
681
682 /* skip the memory */
683 out->method.mem.len += count;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200684
685 /* update printed bytes counter despite we actually printed just a hole */
686 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200687 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200688 case LY_OUT_FD:
689 case LY_OUT_FDSTREAM:
690 case LY_OUT_FILEPATH:
691 case LY_OUT_FILE:
692 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200693 /* buffer the hole */
694 if (out->buf_len + count > out->buf_size) {
695 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
696 if (!out->buffered) {
697 out->buf_len = 0;
698 out->buf_size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200699 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200700 LOGMEM_RET(NULL);
701 }
702 out->buf_size = out->buf_len + count;
703 }
704
705 /* save the current position */
706 *position = out->buf_len;
707
708 /* skip the memory */
709 out->buf_len += count;
710
711 /* increase hole counter */
712 ++out->hole_count;
Radek Krejcia5bba312020-01-09 15:41:20 +0100713
714 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200715 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100716 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200717 }
718
719 return LY_SUCCESS;
720}
721
722LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200723ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200724{
725 LY_ERR ret = LY_SUCCESS;
726
Radek Krejci56cc0872019-04-30 09:22:27 +0200727 LYOUT_CHECK(out, out->status);
728
Radek Krejcid3ca0632019-04-16 16:54:54 +0200729 switch (out->type) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200730 case LY_OUT_MEMORY:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200731 /* write */
Radek Krejcia5bba312020-01-09 15:41:20 +0100732 memcpy(&(*out->method.mem.buf)[position], buf, count);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200733 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200734 case LY_OUT_FD:
735 case LY_OUT_FDSTREAM:
736 case LY_OUT_FILEPATH:
737 case LY_OUT_FILE:
738 case LY_OUT_CALLBACK:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200739 if (out->buf_len < position + count) {
Radek Krejci56cc0872019-04-30 09:22:27 +0200740 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200741 LOGMEM_RET(NULL);
742 }
743
744 /* write into the hole */
745 memcpy(&out->buffered[position], buf, count);
746
747 /* decrease hole counter */
748 --out->hole_count;
749
750 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200751 /* all holes filled, we can write the buffer,
752 * printed bytes counter is updated by ly_write() */
Radek Krejci241f6b52020-05-21 18:13:49 +0200753 ret = ly_write(out, out->buffered, out->buf_len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200754 out->buf_len = 0;
755 }
756 break;
Radek Krejci241f6b52020-05-21 18:13:49 +0200757 case LY_OUT_ERROR:
Radek Krejcia5bba312020-01-09 15:41:20 +0100758 LOGINT(NULL);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200759 }
760
Radek Krejci241f6b52020-05-21 18:13:49 +0200761 if (out->type == LY_OUT_FILEPATH) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100762 /* move the original file descriptor to the end of the output file */
763 lseek(out->method.fdstream.fd, 0, SEEK_END);
764 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200765 return ret;
766}