Coverage Report

Created: 2026-01-13 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/src/out.c
Line
Count
Source
1
/**
2
 * @file out.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @brief libyang output functions.
5
 *
6
 * Copyright (c) 2015 - 2020 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
#define _GNU_SOURCE /* asprintf, strdup */
16
#include <sys/cdefs.h>
17
18
#include "out.h"
19
#include "out_internal.h"
20
21
#include <assert.h>
22
#include <errno.h>
23
#include <stdarg.h>
24
#include <stdint.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <unistd.h>
29
30
#include "common.h"
31
#include "compat.h"
32
#include "log.h"
33
#include "printer_data.h"
34
#include "tree_data.h"
35
#include "tree_schema.h"
36
37
ly_bool
38
ly_should_print(const struct lyd_node *node, uint32_t options)
39
0
{
40
0
    const struct lyd_node *elem;
41
42
0
    if (options & LYD_PRINT_WD_TRIM) {
43
        /* do not print default nodes */
44
0
        if (node->flags & LYD_DEFAULT) {
45
            /* implicit default node/NP container with only default nodes */
46
0
            return 0;
47
0
        } else if (node->schema->nodetype & LYD_NODE_TERM) {
48
0
            if (lyd_is_default(node)) {
49
                /* explicit default node */
50
0
                return 0;
51
0
            }
52
0
        }
53
0
    } else if ((node->flags & LYD_DEFAULT) && !(options & LYD_PRINT_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
54
        /* LYD_PRINT_WD_EXPLICIT, find out if this is some input/output */
55
0
        if (!(node->schema->flags & (LYS_IS_INPUT | LYS_IS_OUTPUT | LYS_IS_NOTIF)) && (node->schema->flags & LYS_CONFIG_W)) {
56
            /* print only if it contains status data in its subtree */
57
0
            LYD_TREE_DFS_BEGIN(node, elem) {
58
0
                if ((elem->schema->nodetype != LYS_CONTAINER) || (elem->schema->flags & LYS_PRESENCE)) {
59
0
                    if (elem->schema->flags & LYS_CONFIG_R) {
60
0
                        return 1;
61
0
                    }
62
0
                }
63
0
                LYD_TREE_DFS_END(node, elem)
64
0
            }
65
0
        }
66
0
        return 0;
67
0
    } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYD_PRINT_KEEPEMPTYCONT)) {
68
        /* avoid empty default containers */
69
0
        LYD_TREE_DFS_BEGIN(node, elem) {
70
0
            if (elem->schema->nodetype != LYS_CONTAINER) {
71
0
                return 1;
72
0
            }
73
0
            assert(elem->flags & LYD_DEFAULT);
74
0
            LYD_TREE_DFS_END(node, elem)
75
0
        }
76
0
        return 0;
77
0
    }
78
79
0
    return 1;
80
0
}
81
82
API LY_OUT_TYPE
83
ly_out_type(const struct ly_out *out)
84
0
{
85
0
    LY_CHECK_ARG_RET(NULL, out, LY_OUT_ERROR);
86
0
    return out->type;
87
0
}
88
89
API LY_ERR
90
ly_out_new_clb(ly_write_clb writeclb, void *user_data, struct ly_out **out)
91
0
{
92
0
    LY_CHECK_ARG_RET(NULL, out, writeclb, LY_EINVAL);
93
94
0
    *out = calloc(1, sizeof **out);
95
0
    LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
96
97
0
    (*out)->type = LY_OUT_CALLBACK;
98
0
    (*out)->method.clb.func = writeclb;
99
0
    (*out)->method.clb.arg = user_data;
100
101
0
    return LY_SUCCESS;
102
0
}
103
104
API ly_write_clb
105
ly_out_clb(struct ly_out *out, ly_write_clb writeclb)
106
0
{
107
0
    void *prev_clb;
108
109
0
    LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
110
111
0
    prev_clb = out->method.clb.func;
112
113
0
    if (writeclb) {
114
0
        out->method.clb.func = writeclb;
115
0
    }
116
117
0
    return prev_clb;
118
0
}
119
120
API void *
121
ly_out_clb_arg(struct ly_out *out, void *arg)
122
0
{
123
0
    void *prev_arg;
124
125
0
    LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_CALLBACK, NULL);
126
127
0
    prev_arg = out->method.clb.arg;
128
129
0
    if (arg) {
130
0
        out->method.clb.arg = arg;
131
0
    }
132
133
0
    return prev_arg;
134
0
}
135
136
API LY_ERR
137
ly_out_new_fd(int fd, struct ly_out **out)
138
0
{
139
0
    LY_CHECK_ARG_RET(NULL, out, fd != -1, LY_EINVAL);
140
141
0
    *out = calloc(1, sizeof **out);
142
0
    LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
143
0
    (*out)->type = LY_OUT_FD;
144
0
    (*out)->method.fd = fd;
145
146
0
    return LY_SUCCESS;
147
0
}
148
149
API int
150
ly_out_fd(struct ly_out *out, int fd)
151
0
{
152
0
    int prev_fd;
153
154
0
    LY_CHECK_ARG_RET(NULL, out, out->type <= LY_OUT_FDSTREAM, -1);
155
156
0
    if (out->type == LY_OUT_FDSTREAM) {
157
0
        prev_fd = out->method.fdstream.fd;
158
0
    } else { /* LY_OUT_FD */
159
0
        prev_fd = out->method.fd;
160
0
    }
161
162
0
    if (fd != -1) {
163
        /* replace output stream */
164
0
        if (out->type == LY_OUT_FDSTREAM) {
165
0
            int streamfd;
166
0
            FILE *stream;
167
168
0
            streamfd = dup(fd);
169
0
            if (streamfd < 0) {
170
0
                LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
171
0
                return -1;
172
0
            }
173
0
            stream = fdopen(streamfd, "a");
174
0
            if (!stream) {
175
0
                LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).", fd, strerror(errno));
176
0
                close(streamfd);
177
0
                return -1;
178
0
            }
179
            /* close only the internally created stream, file descriptor is returned and supposed to be closed by the caller */
180
0
            fclose(out->method.fdstream.f);
181
0
            out->method.fdstream.f = stream;
182
0
            out->method.fdstream.fd = streamfd;
183
0
        } else { /* LY_OUT_FD */
184
0
            out->method.fd = fd;
185
0
        }
186
0
    }
187
188
0
    return prev_fd;
189
0
}
190
191
API LY_ERR
192
ly_out_new_file(FILE *f, struct ly_out **out)
193
0
{
194
0
    LY_CHECK_ARG_RET(NULL, out, f, LY_EINVAL);
195
196
0
    *out = calloc(1, sizeof **out);
197
0
    LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
198
199
0
    (*out)->type = LY_OUT_FILE;
200
0
    (*out)->method.f = f;
201
202
0
    return LY_SUCCESS;
203
0
}
204
205
API FILE *
206
ly_out_file(struct ly_out *out, FILE *f)
207
0
{
208
0
    FILE *prev_f;
209
210
0
    LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILE, NULL);
211
212
0
    prev_f = out->method.f;
213
214
0
    if (f) {
215
0
        out->method.f = f;
216
0
    }
217
218
0
    return prev_f;
219
0
}
220
221
API LY_ERR
222
ly_out_new_memory(char **strp, size_t size, struct ly_out **out)
223
0
{
224
0
    LY_CHECK_ARG_RET(NULL, out, strp, LY_EINVAL);
225
226
0
    *out = calloc(1, sizeof **out);
227
0
    LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
228
229
0
    (*out)->type = LY_OUT_MEMORY;
230
0
    (*out)->method.mem.buf = strp;
231
0
    if (!size) {
232
        /* buffer is supposed to be allocated */
233
0
        *strp = NULL;
234
0
    } else if (*strp) {
235
        /* there is already buffer to use */
236
0
        (*out)->method.mem.size = size;
237
0
    }
238
239
0
    return LY_SUCCESS;
240
0
}
241
242
char *
243
ly_out_memory(struct ly_out *out, char **strp, size_t size)
244
0
{
245
0
    char *data;
246
247
0
    LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_MEMORY, NULL);
248
249
0
    data = *out->method.mem.buf;
250
251
0
    if (strp) {
252
0
        out->method.mem.buf = strp;
253
0
        out->method.mem.len = out->method.mem.size = 0;
254
0
        out->printed = 0;
255
0
        if (!size) {
256
            /* buffer is supposed to be allocated */
257
0
            *strp = NULL;
258
0
        } else if (*strp) {
259
            /* there is already buffer to use */
260
0
            out->method.mem.size = size;
261
0
        }
262
0
    }
263
264
0
    return data;
265
0
}
266
267
API LY_ERR
268
ly_out_reset(struct ly_out *out)
269
0
{
270
0
    LY_CHECK_ARG_RET(NULL, out, LY_EINVAL);
271
272
0
    switch (out->type) {
273
0
    case LY_OUT_ERROR:
274
0
        LOGINT(NULL);
275
0
        return LY_EINT;
276
0
    case LY_OUT_FD:
277
0
        if ((lseek(out->method.fd, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
278
0
            LOGERR(NULL, LY_ESYS, "Seeking output file descriptor failed (%s).", strerror(errno));
279
0
            return LY_ESYS;
280
0
        }
281
0
        if ((errno != ESPIPE) && (ftruncate(out->method.fd, 0) == -1)) {
282
0
            LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
283
0
            return LY_ESYS;
284
0
        }
285
0
        break;
286
0
    case LY_OUT_FDSTREAM:
287
0
    case LY_OUT_FILE:
288
0
    case LY_OUT_FILEPATH:
289
0
        if ((fseek(out->method.f, 0, SEEK_SET) == -1) && (errno != ESPIPE)) {
290
0
            LOGERR(NULL, LY_ESYS, "Seeking output file stream failed (%s).", strerror(errno));
291
0
            return LY_ESYS;
292
0
        }
293
0
        if ((errno != ESPIPE) && (ftruncate(fileno(out->method.f), 0) == -1)) {
294
0
            LOGERR(NULL, LY_ESYS, "Truncating output file failed (%s).", strerror(errno));
295
0
            return LY_ESYS;
296
0
        }
297
0
        break;
298
0
    case LY_OUT_MEMORY:
299
0
        if (out->method.mem.buf && *out->method.mem.buf) {
300
0
            memset(*out->method.mem.buf, 0, out->method.mem.len);
301
0
        }
302
0
        out->printed = 0;
303
0
        out->method.mem.len = 0;
304
0
        break;
305
0
    case LY_OUT_CALLBACK:
306
        /* nothing to do (not seekable) */
307
0
        break;
308
0
    }
309
310
0
    return LY_SUCCESS;
311
0
}
312
313
API LY_ERR
314
ly_out_new_filepath(const char *filepath, struct ly_out **out)
315
0
{
316
0
    LY_CHECK_ARG_RET(NULL, out, filepath, LY_EINVAL);
317
318
0
    *out = calloc(1, sizeof **out);
319
0
    LY_CHECK_ERR_RET(!*out, LOGMEM(NULL), LY_EMEM);
320
321
0
    (*out)->type = LY_OUT_FILEPATH;
322
0
    (*out)->method.fpath.f = fopen(filepath, "w");
323
0
    if (!(*out)->method.fpath.f) {
324
0
        LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
325
0
        free(*out);
326
0
        *out = NULL;
327
0
        return LY_ESYS;
328
0
    }
329
0
    (*out)->method.fpath.filepath = strdup(filepath);
330
0
    return LY_SUCCESS;
331
0
}
332
333
API const char *
334
ly_out_filepath(struct ly_out *out, const char *filepath)
335
0
{
336
0
    FILE *f;
337
338
0
    LY_CHECK_ARG_RET(NULL, out, out->type == LY_OUT_FILEPATH, filepath ? NULL : ((void *)-1));
339
340
0
    if (!filepath) {
341
0
        return out->method.fpath.filepath;
342
0
    }
343
344
    /* replace filepath */
345
0
    f = out->method.fpath.f;
346
0
    out->method.fpath.f = fopen(filepath, "w");
347
0
    if (!out->method.fpath.f) {
348
0
        LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", filepath, strerror(errno));
349
0
        out->method.fpath.f = f;
350
0
        return (void *)-1;
351
0
    }
352
0
    fclose(f);
353
0
    free(out->method.fpath.filepath);
354
0
    out->method.fpath.filepath = strdup(filepath);
355
356
0
    return NULL;
357
0
}
358
359
API void
360
ly_out_free(struct ly_out *out, void (*clb_arg_destructor)(void *arg), ly_bool destroy)
361
0
{
362
0
    if (!out) {
363
0
        return;
364
0
    }
365
366
0
    switch (out->type) {
367
0
    case LY_OUT_CALLBACK:
368
0
        if (clb_arg_destructor) {
369
0
            clb_arg_destructor(out->method.clb.arg);
370
0
        }
371
0
        break;
372
0
    case LY_OUT_FDSTREAM:
373
0
        fclose(out->method.fdstream.f);
374
0
        if (destroy) {
375
0
            close(out->method.fdstream.fd);
376
0
        }
377
0
        break;
378
0
    case LY_OUT_FD:
379
0
        if (destroy) {
380
0
            close(out->method.fd);
381
0
        }
382
0
        break;
383
0
    case LY_OUT_FILE:
384
0
        if (destroy) {
385
0
            fclose(out->method.f);
386
0
        }
387
0
        break;
388
0
    case LY_OUT_MEMORY:
389
0
        if (destroy) {
390
0
            free(*out->method.mem.buf);
391
0
        }
392
0
        break;
393
0
    case LY_OUT_FILEPATH:
394
0
        free(out->method.fpath.filepath);
395
0
        fclose(out->method.fpath.f);
396
0
        break;
397
0
    case LY_OUT_ERROR:
398
0
        LOGINT(NULL);
399
0
    }
400
401
0
    free(out->buffered);
402
0
    free(out);
403
0
}
404
405
static LY_ERR
406
ly_vprint_(struct ly_out *out, const char *format, va_list ap)
407
0
{
408
0
    LY_ERR ret;
409
0
    int written = 0;
410
0
    char *msg = NULL, *aux;
411
412
0
    switch (out->type) {
413
0
    case LY_OUT_FD:
414
0
        written = vdprintf(out->method.fd, format, ap);
415
0
        break;
416
0
    case LY_OUT_FDSTREAM:
417
0
    case LY_OUT_FILEPATH:
418
0
    case LY_OUT_FILE:
419
0
        written = vfprintf(out->method.f, format, ap);
420
0
        break;
421
0
    case LY_OUT_MEMORY:
422
0
        if ((written = vasprintf(&msg, format, ap)) < 0) {
423
0
            break;
424
0
        }
425
0
        if (out->method.mem.len + written + 1 > out->method.mem.size) {
426
0
            aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
427
0
            if (!aux) {
428
0
                out->method.mem.buf = NULL;
429
0
                out->method.mem.len = 0;
430
0
                out->method.mem.size = 0;
431
0
                LOGMEM(NULL);
432
0
                return LY_EMEM;
433
0
            }
434
0
            *out->method.mem.buf = aux;
435
0
            out->method.mem.size = out->method.mem.len + written + 1;
436
0
        }
437
0
        memcpy(&(*out->method.mem.buf)[out->method.mem.len], msg, written);
438
0
        out->method.mem.len += written;
439
0
        (*out->method.mem.buf)[out->method.mem.len] = '\0';
440
0
        free(msg);
441
0
        break;
442
0
    case LY_OUT_CALLBACK:
443
0
        if ((written = vasprintf(&msg, format, ap)) < 0) {
444
0
            break;
445
0
        }
446
0
        written = out->method.clb.func(out->method.clb.arg, msg, written);
447
0
        free(msg);
448
0
        break;
449
0
    case LY_OUT_ERROR:
450
0
        LOGINT(NULL);
451
0
        return LY_EINT;
452
0
    }
453
454
0
    if (written < 0) {
455
0
        LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
456
0
        written = 0;
457
0
        ret = LY_ESYS;
458
0
    } else {
459
0
        if (out->type == LY_OUT_FDSTREAM) {
460
            /* move the original file descriptor to the end of the output file */
461
0
            lseek(out->method.fdstream.fd, 0, SEEK_END);
462
0
        }
463
0
        ret = LY_SUCCESS;
464
0
    }
465
466
0
    out->printed += written;
467
0
    out->func_printed += written;
468
0
    return ret;
469
0
}
470
471
LY_ERR
472
ly_print_(struct ly_out *out, const char *format, ...)
473
0
{
474
0
    LY_ERR ret;
475
0
    va_list ap;
476
477
0
    va_start(ap, format);
478
0
    ret = ly_vprint_(out, format, ap);
479
0
    va_end(ap);
480
481
0
    return ret;
482
0
}
483
484
API LY_ERR
485
ly_print(struct ly_out *out, const char *format, ...)
486
0
{
487
0
    LY_ERR ret;
488
0
    va_list ap;
489
490
0
    out->func_printed = 0;
491
492
0
    va_start(ap, format);
493
0
    ret = ly_vprint_(out, format, ap);
494
0
    va_end(ap);
495
496
0
    return ret;
497
0
}
498
499
API void
500
ly_print_flush(struct ly_out *out)
501
0
{
502
0
    switch (out->type) {
503
0
    case LY_OUT_FDSTREAM:
504
        /* move the original file descriptor to the end of the output file */
505
0
        lseek(out->method.fdstream.fd, 0, SEEK_END);
506
0
        fflush(out->method.fdstream.f);
507
0
        break;
508
0
    case LY_OUT_FILEPATH:
509
0
    case LY_OUT_FILE:
510
0
        fflush(out->method.f);
511
0
        break;
512
0
    case LY_OUT_FD:
513
0
        fsync(out->method.fd);
514
0
        break;
515
0
    case LY_OUT_MEMORY:
516
0
    case LY_OUT_CALLBACK:
517
        /* nothing to do */
518
0
        break;
519
0
    case LY_OUT_ERROR:
520
0
        LOGINT(NULL);
521
0
    }
522
523
0
    free(out->buffered);
524
0
    out->buf_size = out->buf_len = 0;
525
0
}
526
527
LY_ERR
528
ly_write_(struct ly_out *out, const char *buf, size_t len)
529
0
{
530
0
    LY_ERR ret = LY_SUCCESS;
531
0
    size_t written = 0;
532
533
0
    if (out->hole_count) {
534
        /* we are buffering data after a hole */
535
0
        if (out->buf_len + len > out->buf_size) {
536
0
            out->buffered = ly_realloc(out->buffered, out->buf_len + len);
537
0
            if (!out->buffered) {
538
0
                out->buf_len = 0;
539
0
                out->buf_size = 0;
540
0
                LOGMEM(NULL);
541
0
                return LY_EMEM;
542
0
            }
543
0
            out->buf_size = out->buf_len + len;
544
0
        }
545
546
0
        memcpy(&out->buffered[out->buf_len], buf, len);
547
0
        out->buf_len += len;
548
549
0
        out->printed += len;
550
0
        out->func_printed += len;
551
0
        return LY_SUCCESS;
552
0
    }
553
554
0
repeat:
555
0
    switch (out->type) {
556
0
    case LY_OUT_MEMORY:
557
0
        if (out->method.mem.len + len + 1 > out->method.mem.size) {
558
0
            *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + len + 1);
559
0
            if (!*out->method.mem.buf) {
560
0
                out->method.mem.len = 0;
561
0
                out->method.mem.size = 0;
562
0
                LOGMEM(NULL);
563
0
                return LY_EMEM;
564
0
            }
565
0
            out->method.mem.size = out->method.mem.len + len + 1;
566
0
        }
567
0
        memcpy(&(*out->method.mem.buf)[out->method.mem.len], buf, len);
568
0
        out->method.mem.len += len;
569
0
        (*out->method.mem.buf)[out->method.mem.len] = '\0';
570
571
0
        written = len;
572
0
        break;
573
0
    case LY_OUT_FD: {
574
0
        ssize_t r;
575
0
        r = write(out->method.fd, buf, len);
576
0
        if (r < 0) {
577
0
            ret = LY_ESYS;
578
0
        } else {
579
0
            written = (size_t)r;
580
0
        }
581
0
        break;
582
0
    }
583
0
    case LY_OUT_FDSTREAM:
584
0
    case LY_OUT_FILEPATH:
585
0
    case LY_OUT_FILE:
586
0
        written = fwrite(buf, sizeof *buf, len, out->method.f);
587
0
        if (written != len) {
588
0
            ret = LY_ESYS;
589
0
        }
590
0
        break;
591
0
    case LY_OUT_CALLBACK: {
592
0
        ssize_t r;
593
0
        r = out->method.clb.func(out->method.clb.arg, buf, len);
594
0
        if (r < 0) {
595
0
            ret = LY_ESYS;
596
0
        } else {
597
0
            written = (size_t)r;
598
0
        }
599
0
        break;
600
0
    }
601
0
    case LY_OUT_ERROR:
602
0
        LOGINT(NULL);
603
0
        return LY_EINT;
604
0
    }
605
606
0
    if (ret) {
607
0
        if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
608
0
            ret = LY_SUCCESS;
609
0
            goto repeat;
610
0
        }
611
0
        LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
612
0
        written = 0;
613
0
    } else if ((size_t)written != len) {
614
0
        LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
615
0
                len - (size_t)written, len);
616
0
        ret = LY_ESYS;
617
0
    } else {
618
0
        if (out->type == LY_OUT_FDSTREAM) {
619
            /* move the original file descriptor to the end of the output file */
620
0
            lseek(out->method.fdstream.fd, 0, SEEK_END);
621
0
        }
622
0
        ret = LY_SUCCESS;
623
0
    }
624
625
0
    out->printed += written;
626
0
    out->func_printed += written;
627
0
    return ret;
628
0
}
629
630
API LY_ERR
631
ly_write(struct ly_out *out, const char *buf, size_t len)
632
0
{
633
0
    out->func_printed = 0;
634
635
0
    return ly_write_(out, buf, len);
636
0
}
637
638
API size_t
639
ly_out_printed(const struct ly_out *out)
640
0
{
641
0
    return out->func_printed;
642
0
}
643
644
LY_ERR
645
ly_write_skip(struct ly_out *out, size_t count, size_t *position)
646
0
{
647
0
    switch (out->type) {
648
0
    case LY_OUT_MEMORY:
649
0
        if (out->method.mem.len + count > out->method.mem.size) {
650
0
            *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + count);
651
0
            if (!(*out->method.mem.buf)) {
652
0
                out->method.mem.len = 0;
653
0
                out->method.mem.size = 0;
654
0
                LOGMEM(NULL);
655
0
                return LY_EMEM;
656
0
            }
657
0
            out->method.mem.size = out->method.mem.len + count;
658
0
        }
659
660
        /* save the current position */
661
0
        *position = out->method.mem.len;
662
663
        /* skip the memory */
664
0
        out->method.mem.len += count;
665
0
        break;
666
0
    case LY_OUT_FD:
667
0
    case LY_OUT_FDSTREAM:
668
0
    case LY_OUT_FILEPATH:
669
0
    case LY_OUT_FILE:
670
0
    case LY_OUT_CALLBACK:
671
        /* buffer the hole */
672
0
        if (out->buf_len + count > out->buf_size) {
673
0
            out->buffered = ly_realloc(out->buffered, out->buf_len + count);
674
0
            if (!out->buffered) {
675
0
                out->buf_len = 0;
676
0
                out->buf_size = 0;
677
0
                LOGMEM(NULL);
678
0
                return LY_EMEM;
679
0
            }
680
0
            out->buf_size = out->buf_len + count;
681
0
        }
682
683
        /* save the current position */
684
0
        *position = out->buf_len;
685
686
        /* skip the memory */
687
0
        out->buf_len += count;
688
689
        /* increase hole counter */
690
0
        ++out->hole_count;
691
0
        break;
692
0
    case LY_OUT_ERROR:
693
0
        LOGINT(NULL);
694
0
        return LY_EINT;
695
0
    }
696
697
    /* update printed bytes counter despite we actually printed just a hole */
698
0
    out->printed += count;
699
0
    out->func_printed += count;
700
0
    return LY_SUCCESS;
701
0
}
702
703
LY_ERR
704
ly_write_skipped(struct ly_out *out, size_t position, const char *buf, size_t count)
705
0
{
706
0
    LY_ERR ret = LY_SUCCESS;
707
708
0
    switch (out->type) {
709
0
    case LY_OUT_MEMORY:
710
        /* write */
711
0
        memcpy(&(*out->method.mem.buf)[position], buf, count);
712
0
        break;
713
0
    case LY_OUT_FD:
714
0
    case LY_OUT_FDSTREAM:
715
0
    case LY_OUT_FILEPATH:
716
0
    case LY_OUT_FILE:
717
0
    case LY_OUT_CALLBACK:
718
0
        if (out->buf_len < position + count) {
719
0
            LOGMEM(NULL);
720
0
            return LY_EMEM;
721
0
        }
722
723
        /* write into the hole */
724
0
        memcpy(&out->buffered[position], buf, count);
725
726
        /* decrease hole counter */
727
0
        --out->hole_count;
728
729
0
        if (!out->hole_count) {
730
            /* all holes filled, we can write the buffer,
731
             * printed bytes counter is updated by ly_write_() */
732
0
            ret = ly_write_(out, out->buffered, out->buf_len);
733
0
            out->buf_len = 0;
734
0
        }
735
0
        break;
736
0
    case LY_OUT_ERROR:
737
0
        LOGINT(NULL);
738
0
        return LY_EINT;
739
0
    }
740
741
0
    if (out->type == LY_OUT_FILEPATH) {
742
        /* move the original file descriptor to the end of the output file */
743
        lseek(out->method.fdstream.fd, 0, SEEK_END);
744
0
    }
745
0
    return ret;
746
0
}