Coverage Report

Created: 2026-03-31 06:47

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