Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavutil/bprint.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2012 Nicolas George
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#include <limits.h>
22
#include <stdarg.h>
23
#include <stdio.h>
24
#include <string.h>
25
#include <time.h>
26
#include "avstring.h"
27
#include "bprint.h"
28
#include "error.h"
29
#include "macros.h"
30
#include "mem.h"
31
32
0
#define av_bprint_room(buf) ((buf)->size - FFMIN((buf)->len, (buf)->size))
33
0
#define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer)
34
35
static int av_bprint_alloc(AVBPrint *buf, unsigned room)
36
0
{
37
0
    char *old_str, *new_str;
38
0
    unsigned min_size, new_size;
39
40
0
    if (buf->size == buf->size_max)
41
0
        return AVERROR(EIO);
42
0
    if (!av_bprint_is_complete(buf))
43
0
        return AVERROR_INVALIDDATA; /* it is already truncated anyway */
44
0
    min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room);
45
0
    new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2;
46
0
    if (new_size < min_size)
47
0
        new_size = FFMIN(buf->size_max, min_size);
48
0
    old_str = av_bprint_is_allocated(buf) ? buf->str : NULL;
49
0
    new_str = av_realloc(old_str, new_size);
50
0
    if (!new_str)
51
0
        return AVERROR(ENOMEM);
52
0
    if (!old_str)
53
0
        memcpy(new_str, buf->str, buf->len + 1);
54
0
    buf->str  = new_str;
55
0
    buf->size = new_size;
56
0
    return 0;
57
0
}
58
59
static void av_bprint_grow(AVBPrint *buf, unsigned extra_len)
60
0
{
61
    /* arbitrary margin to avoid small overflows */
62
0
    extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len);
63
0
    buf->len += extra_len;
64
0
    if (buf->size)
65
0
        buf->str[FFMIN(buf->len, buf->size - 1)] = 0;
66
0
}
67
68
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
69
0
{
70
0
    unsigned size_auto = (char *)buf + sizeof(*buf) -
71
0
                         buf->reserved_internal_buffer;
72
73
0
    if (size_max == AV_BPRINT_SIZE_AUTOMATIC)
74
0
        size_max = size_auto;
75
0
    buf->str      = buf->reserved_internal_buffer;
76
0
    buf->len      = 0;
77
0
    buf->size     = FFMIN(size_auto, size_max);
78
0
    buf->size_max = size_max;
79
0
    *buf->str = 0;
80
0
    if (size_init > buf->size)
81
0
        av_bprint_alloc(buf, size_init - 1);
82
0
}
83
84
void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size)
85
0
{
86
0
    if (size == 0) {
87
0
        av_bprint_init(buf, 0, AV_BPRINT_SIZE_COUNT_ONLY);
88
0
        return;
89
0
    }
90
91
0
    buf->str      = buffer;
92
0
    buf->len      = 0;
93
0
    buf->size     = size;
94
0
    buf->size_max = size;
95
0
    *buf->str = 0;
96
0
}
97
98
void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg)
99
0
{
100
0
    unsigned room;
101
0
    char *dst;
102
0
    int extra_len;
103
0
    va_list vl;
104
105
0
    while (1) {
106
0
        room = av_bprint_room(buf);
107
0
        dst = room ? buf->str + buf->len : NULL;
108
0
        va_copy(vl, vl_arg);
109
0
        extra_len = vsnprintf(dst, room, fmt, vl);
110
0
        va_end(vl);
111
0
        if (extra_len <= 0)
112
0
            return;
113
0
        if (extra_len < room)
114
0
            break;
115
0
        if (av_bprint_alloc(buf, extra_len))
116
0
            break;
117
0
    }
118
0
    av_bprint_grow(buf, extra_len);
119
0
}
120
121
void av_bprintf(AVBPrint *buf, const char *fmt, ...)
122
0
{
123
0
    va_list vl;
124
0
    va_start(vl, fmt);
125
0
    av_vbprintf(buf, fmt, vl);
126
0
    va_end(vl);
127
0
}
128
129
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
130
0
{
131
0
    unsigned room, real_n;
132
133
0
    while (1) {
134
0
        room = av_bprint_room(buf);
135
0
        if (n < room)
136
0
            break;
137
0
        if (av_bprint_alloc(buf, n))
138
0
            break;
139
0
    }
140
0
    if (room) {
141
0
        real_n = FFMIN(n, room - 1);
142
0
        memset(buf->str + buf->len, c, real_n);
143
0
    }
144
0
    av_bprint_grow(buf, n);
145
0
}
146
147
void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)
148
0
{
149
0
    unsigned room, real_n;
150
151
0
    while (1) {
152
0
        room = av_bprint_room(buf);
153
0
        if (size < room)
154
0
            break;
155
0
        if (av_bprint_alloc(buf, size))
156
0
            break;
157
0
    }
158
0
    if (room) {
159
0
        real_n = FFMIN(size, room - 1);
160
0
        memcpy(buf->str + buf->len, data, real_n);
161
0
    }
162
0
    av_bprint_grow(buf, size);
163
0
}
164
165
void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm)
166
0
{
167
0
    unsigned room;
168
0
    size_t l;
169
0
    size_t fmt_len = strlen(fmt);
170
171
0
    if (!*fmt)
172
0
        return;
173
0
    while (1) {
174
0
        room = av_bprint_room(buf);
175
0
        if (room && (l = strftime(buf->str + buf->len, room, fmt, tm)))
176
0
            break;
177
178
        /* Due to the limitations of strftime() it is not possible to know if
179
         * the output buffer is too small or the output is empty.
180
         * However, a 256x output space requirement compared to the format
181
         * string length is so unlikely we can safely assume empty output. This
182
         * allows supporting possibly empty format strings like "%p". */
183
0
        if (room >> 8 > fmt_len)
184
0
            break;
185
186
        /* strftime does not tell us how much room it would need: let us
187
           retry with twice as much until the buffer is large enough */
188
0
        room = !room ? fmt_len + 1 :
189
0
               room <= INT_MAX / 2 ? room * 2 : INT_MAX;
190
0
        if (av_bprint_alloc(buf, room)) {
191
            /* impossible to grow, try to manage something useful anyway */
192
0
            room = av_bprint_room(buf);
193
0
            if (room < 1024) {
194
                /* if strftime fails because the buffer has (almost) reached
195
                   its maximum size, let us try in a local buffer; 1k should
196
                   be enough to format any real date+time string */
197
0
                char buf2[1024];
198
0
                if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) {
199
0
                    av_bprintf(buf, "%s", buf2);
200
0
                    return;
201
0
                }
202
0
            }
203
0
            if (room) {
204
                /* if anything else failed and the buffer is not already
205
                   truncated, let us add a stock string and force truncation */
206
0
                static const char txt[] = "[truncated strftime output]";
207
0
                memset(buf->str + buf->len, '!', room);
208
0
                memcpy(buf->str + buf->len, txt, FFMIN(sizeof(txt) - 1, room));
209
0
                av_bprint_grow(buf, room); /* force truncation */
210
0
            }
211
0
            return;
212
0
        }
213
0
    }
214
0
    av_bprint_grow(buf, l);
215
0
}
216
217
void av_bprint_get_buffer(AVBPrint *buf, unsigned size,
218
                          unsigned char **mem, unsigned *actual_size)
219
0
{
220
0
    if (size > av_bprint_room(buf))
221
0
        av_bprint_alloc(buf, size);
222
0
    *actual_size = av_bprint_room(buf);
223
0
    *mem = *actual_size ? (unsigned char *) (buf->str + buf->len) : NULL;
224
0
}
225
226
void av_bprint_clear(AVBPrint *buf)
227
0
{
228
0
    if (buf->len) {
229
0
        *buf->str = 0;
230
0
        buf->len  = 0;
231
0
    }
232
0
}
233
234
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
235
0
{
236
0
    unsigned real_size = FFMIN(buf->len + 1, buf->size);
237
0
    char *str;
238
0
    int ret = 0;
239
240
0
    if (ret_str) {
241
0
        if (av_bprint_is_allocated(buf)) {
242
0
            str = av_realloc(buf->str, real_size);
243
0
            if (!str)
244
0
                str = buf->str;
245
0
            buf->str = NULL;
246
0
        } else {
247
0
            str = av_memdup(buf->str, real_size);
248
0
            if (!str)
249
0
                ret = AVERROR(ENOMEM);
250
0
        }
251
0
        *ret_str = str;
252
0
    } else {
253
0
        if (av_bprint_is_allocated(buf))
254
0
            av_freep(&buf->str);
255
0
    }
256
0
    buf->size = real_size;
257
0
    return ret;
258
0
}
259
260
0
#define WHITESPACES " \n\t\r"
261
262
void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
263
                      enum AVEscapeMode mode, int flags)
264
0
{
265
0
    const char *src0 = src;
266
267
0
    if (mode == AV_ESCAPE_MODE_AUTO)
268
0
        mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */
269
270
0
    switch (mode) {
271
0
    case AV_ESCAPE_MODE_QUOTE:
272
        /* enclose the string between '' */
273
0
        av_bprint_chars(dstbuf, '\'', 1);
274
0
        for (; *src; src++) {
275
0
            if (*src == '\'')
276
0
                av_bprintf(dstbuf, "'\\''");
277
0
            else
278
0
                av_bprint_chars(dstbuf, *src, 1);
279
0
        }
280
0
        av_bprint_chars(dstbuf, '\'', 1);
281
0
        break;
282
283
0
    case AV_ESCAPE_MODE_XML:
284
        /* escape XML non-markup character data as per 2.4 by default: */
285
        /*  [^<&]* - ([^<&]* ']]>' [^<&]*) */
286
287
        /* additionally, given one of the AV_ESCAPE_FLAG_XML_* flags, */
288
        /* escape those specific characters as required. */
289
0
        for (; *src; src++) {
290
0
            switch (*src) {
291
0
            case '&' : av_bprintf(dstbuf, "%s", "&amp;");  break;
292
0
            case '<' : av_bprintf(dstbuf, "%s", "&lt;");   break;
293
0
            case '>' : av_bprintf(dstbuf, "%s", "&gt;");   break;
294
0
            case '\'':
295
0
                if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES))
296
0
                    goto XML_DEFAULT_HANDLING;
297
298
0
                av_bprintf(dstbuf, "%s", "&apos;");
299
0
                break;
300
0
            case '"' :
301
0
                if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES))
302
0
                    goto XML_DEFAULT_HANDLING;
303
304
0
                av_bprintf(dstbuf, "%s", "&quot;");
305
0
                break;
306
0
XML_DEFAULT_HANDLING:
307
0
            default: av_bprint_chars(dstbuf, *src, 1);
308
0
            }
309
0
        }
310
0
        break;
311
312
    /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */
313
0
    default:
314
        /* \-escape characters */
315
0
        for (; *src; src++) {
316
0
            int is_first_last       = src == src0 || !*(src+1);
317
0
            int is_ws               = !!strchr(WHITESPACES, *src);
318
0
            int is_strictly_special = special_chars && strchr(special_chars, *src);
319
0
            int is_special          =
320
0
                is_strictly_special || strchr("'\\", *src) ||
321
0
                (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE));
322
323
0
            if (is_strictly_special ||
324
0
                (!(flags & AV_ESCAPE_FLAG_STRICT) &&
325
0
                 (is_special || (is_ws && is_first_last))))
326
0
                av_bprint_chars(dstbuf, '\\', 1);
327
0
            av_bprint_chars(dstbuf, *src, 1);
328
0
        }
329
0
        break;
330
0
    }
331
0
}