FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/bprint.c
Date: 2026-09-11 02:21:26
Exec Total Coverage
Lines: 158 182 86.8%
Functions: 13 13 100.0%
Branches: 97 127 76.4%

Line Branch Exec 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 #define av_bprint_room(buf) ((buf)->size - FFMIN((buf)->len, (buf)->size))
33 #define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer)
34
35 70619 static int av_bprint_alloc(AVBPrint *buf, unsigned room)
36 {
37 char *old_str, *new_str;
38 unsigned min_size, new_size;
39
40
2/2
✓ Branch 0 taken 70432 times.
✓ Branch 1 taken 187 times.
70619 if (buf->size == buf->size_max)
41 70432 return AVERROR(EIO);
42
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
187 if (!av_bprint_is_complete(buf))
43 return AVERROR_INVALIDDATA; /* it is already truncated anyway */
44 187 min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room);
45
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 186 times.
187 new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2;
46
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 159 times.
187 if (new_size < min_size)
47 28 new_size = FFMIN(buf->size_max, min_size);
48
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 82 times.
187 old_str = av_bprint_is_allocated(buf) ? buf->str : NULL;
49 187 new_str = av_realloc(old_str, new_size);
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 187 times.
187 if (!new_str)
51 return AVERROR(ENOMEM);
52
2/2
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 105 times.
187 if (!old_str)
53 82 memcpy(new_str, buf->str, buf->len + 1);
54 187 buf->str = new_str;
55 187 buf->size = new_size;
56 187 return 0;
57 }
58
59 286573983 static void av_bprint_grow(AVBPrint *buf, unsigned extra_len)
60 {
61 /* arbitrary margin to avoid small overflows */
62 286573983 extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len);
63 286573983 buf->len += extra_len;
64
2/2
✓ Branch 0 taken 286504233 times.
✓ Branch 1 taken 69750 times.
286573983 if (buf->size)
65 286504233 buf->str[FFMIN(buf->len, buf->size - 1)] = 0;
66 286573983 }
67
68 3962510 void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
69 {
70 3962510 unsigned size_auto = (char *)buf + sizeof(*buf) -
71 buf->reserved_internal_buffer;
72
73
2/2
✓ Branch 0 taken 3257475 times.
✓ Branch 1 taken 705035 times.
3962510 if (size_max == AV_BPRINT_SIZE_AUTOMATIC)
74 3257475 size_max = size_auto;
75 3962510 buf->str = buf->reserved_internal_buffer;
76 3962510 buf->len = 0;
77 3962510 buf->size = FFMIN(size_auto, size_max);
78 3962510 buf->size_max = size_max;
79 3962510 *buf->str = 0;
80
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 3962465 times.
3962510 if (size_init > buf->size)
81 45 av_bprint_alloc(buf, size_init - 1);
82 3962510 }
83
84 1173965 void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size)
85 {
86
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1173933 times.
1173965 if (size == 0) {
87 32 av_bprint_init(buf, 0, AV_BPRINT_SIZE_COUNT_ONLY);
88 32 return;
89 }
90
91 1173933 buf->str = buffer;
92 1173933 buf->len = 0;
93 1173933 buf->size = size;
94 1173933 buf->size_max = size;
95 1173933 *buf->str = 0;
96 }
97
98 274818889 void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg)
99 {
100 unsigned room;
101 char *dst;
102 int extra_len;
103 va_list vl;
104
105 while (1) {
106 274818914 room = av_bprint_room(buf);
107
2/2
✓ Branch 0 taken 274817467 times.
✓ Branch 1 taken 1447 times.
274818914 dst = room ? buf->str + buf->len : NULL;
108 274818914 va_copy(vl, vl_arg);
109 274818914 extra_len = vsnprintf(dst, room, fmt, vl);
110 274818914 va_end(vl);
111
2/2
✓ Branch 0 taken 2526670 times.
✓ Branch 1 taken 272292244 times.
274818914 if (extra_len <= 0)
112 2526670 return;
113
2/2
✓ Branch 0 taken 272290768 times.
✓ Branch 1 taken 1476 times.
272292244 if (extra_len < room)
114 272290768 break;
115
2/2
✓ Branch 1 taken 1451 times.
✓ Branch 2 taken 25 times.
1476 if (av_bprint_alloc(buf, extra_len))
116 1451 break;
117 }
118 272292219 av_bprint_grow(buf, extra_len);
119 }
120
121 274318752 void av_bprintf(AVBPrint *buf, const char *fmt, ...)
122 {
123 va_list vl;
124 274318752 va_start(vl, fmt);
125 274318752 av_vbprintf(buf, fmt, vl);
126 274318752 va_end(vl);
127 274318752 }
128
129 14002184 void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
130 {
131 unsigned room, real_n;
132
133 while (1) {
134 14002184 room = av_bprint_room(buf);
135
2/2
✓ Branch 0 taken 13933204 times.
✓ Branch 1 taken 68980 times.
14002184 if (n < room)
136 13933204 break;
137
1/2
✓ Branch 1 taken 68980 times.
✗ Branch 2 not taken.
68980 if (av_bprint_alloc(buf, n))
138 68980 break;
139 }
140
2/2
✓ Branch 0 taken 13933204 times.
✓ Branch 1 taken 68980 times.
14002184 if (room) {
141 13933204 real_n = FFMIN(n, room - 1);
142 13933204 memset(buf->str + buf->len, c, real_n);
143 }
144 14002184 av_bprint_grow(buf, n);
145 14002184 }
146
147 279579 void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)
148 {
149 unsigned room, real_n;
150
151 while (1) {
152 279682 room = av_bprint_room(buf);
153
2/2
✓ Branch 0 taken 279579 times.
✓ Branch 1 taken 103 times.
279682 if (size < room)
154 279579 break;
155
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 103 times.
103 if (av_bprint_alloc(buf, size))
156 break;
157 }
158
1/2
✓ Branch 0 taken 279579 times.
✗ Branch 1 not taken.
279579 if (room) {
159 279579 real_n = FFMIN(size, room - 1);
160 279579 memcpy(buf->str + buf->len, data, real_n);
161 }
162 279579 av_bprint_grow(buf, size);
163 279579 }
164
165 2 void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm)
166 {
167 unsigned room;
168 size_t l;
169 2 size_t fmt_len = strlen(fmt);
170
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!*fmt)
172 return;
173 while (1) {
174 2 room = av_bprint_room(buf);
175
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if (room && (l = strftime(buf->str + buf->len, room, fmt, tm)))
176 1 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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (room >> 8 > fmt_len)
184 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
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 room = !room ? fmt_len + 1 :
189 room <= INT_MAX / 2 ? room * 2 : INT_MAX;
190
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (av_bprint_alloc(buf, room)) {
191 /* impossible to grow, try to manage something useful anyway */
192 1 room = av_bprint_room(buf);
193
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 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 char buf2[1024];
198
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) {
199 1 av_bprintf(buf, "%s", buf2);
200 1 return;
201 }
202 }
203 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 static const char txt[] = "[truncated strftime output]";
207 memset(buf->str + buf->len, '!', room);
208 memcpy(buf->str + buf->len, txt, FFMIN(sizeof(txt) - 1, room));
209 av_bprint_grow(buf, room); /* force truncation */
210 }
211 return;
212 }
213 }
214 1 av_bprint_grow(buf, l);
215 }
216
217 21 void av_bprint_get_buffer(AVBPrint *buf, unsigned size,
218 unsigned char **mem, unsigned *actual_size)
219 {
220
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
21 if (size > av_bprint_room(buf))
221 14 av_bprint_alloc(buf, size);
222 21 *actual_size = av_bprint_room(buf);
223
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 *mem = *actual_size ? (unsigned char *) (buf->str + buf->len) : NULL;
224 21 }
225
226 18050996 void av_bprint_clear(AVBPrint *buf)
227 {
228
2/2
✓ Branch 0 taken 16755983 times.
✓ Branch 1 taken 1295013 times.
18050996 if (buf->len) {
229 16755983 *buf->str = 0;
230 16755983 buf->len = 0;
231 }
232 18050996 }
233
234 821928 int av_bprint_finalize(AVBPrint *buf, char **ret_str)
235 {
236 821928 unsigned real_size = FFMIN(buf->len + 1, buf->size);
237 char *str;
238 821928 int ret = 0;
239
240
2/2
✓ Branch 0 taken 15791 times.
✓ Branch 1 taken 806137 times.
821928 if (ret_str) {
241
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 15716 times.
15791 if (av_bprint_is_allocated(buf)) {
242 75 str = av_realloc(buf->str, real_size);
243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 if (!str)
244 str = buf->str;
245 75 buf->str = NULL;
246 } else {
247 15716 str = av_memdup(buf->str, real_size);
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15716 times.
15716 if (!str)
249 ret = AVERROR(ENOMEM);
250 }
251 15791 *ret_str = str;
252 } else {
253
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 806126 times.
806137 if (av_bprint_is_allocated(buf))
254 11 av_freep(&buf->str);
255 }
256 821928 buf->size = real_size;
257 821928 return ret;
258 }
259
260 #define WHITESPACES " \n\t\r"
261
262 10769 void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
263 enum AVEscapeMode mode, int flags)
264 {
265 10769 const char *src0 = src;
266
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10769 times.
10769 if (mode == AV_ESCAPE_MODE_AUTO)
268 mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */
269
270
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 1293 times.
✓ Branch 2 taken 9476 times.
10769 switch (mode) {
271 case AV_ESCAPE_MODE_QUOTE:
272 /* enclose the string between '' */
273 av_bprint_chars(dstbuf, '\'', 1);
274 for (; *src; src++) {
275 if (*src == '\'')
276 av_bprintf(dstbuf, "'\\''");
277 else
278 av_bprint_chars(dstbuf, *src, 1);
279 }
280 av_bprint_chars(dstbuf, '\'', 1);
281 break;
282
283 1293 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
2/2
✓ Branch 0 taken 21205 times.
✓ Branch 1 taken 1293 times.
22498 for (; *src; src++) {
290
6/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 39 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 21131 times.
21205 switch (*src) {
291 2 case '&' : av_bprintf(dstbuf, "%s", "&amp;"); break;
292 17 case '<' : av_bprintf(dstbuf, "%s", "&lt;"); break;
293 12 case '>' : av_bprintf(dstbuf, "%s", "&gt;"); break;
294 39 case '\'':
295
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES))
296 39 goto XML_DEFAULT_HANDLING;
297
298 av_bprintf(dstbuf, "%s", "&apos;");
299 break;
300 4 case '"' :
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES))
302 goto XML_DEFAULT_HANDLING;
303
304 4 av_bprintf(dstbuf, "%s", "&quot;");
305 4 break;
306 39 XML_DEFAULT_HANDLING:
307 21170 default: av_bprint_chars(dstbuf, *src, 1);
308 }
309 }
310 1293 break;
311
312 /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */
313 9476 default:
314 /* \-escape characters */
315
2/2
✓ Branch 0 taken 231629 times.
✓ Branch 1 taken 9476 times.
241105 for (; *src; src++) {
316
4/4
✓ Branch 0 taken 222161 times.
✓ Branch 1 taken 9468 times.
✓ Branch 2 taken 9460 times.
✓ Branch 3 taken 212701 times.
231629 int is_first_last = src == src0 || !*(src+1);
317 231629 int is_ws = !!strchr(WHITESPACES, *src);
318
3/4
✓ Branch 0 taken 231629 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
✓ Branch 3 taken 231559 times.
231629 int is_strictly_special = special_chars && strchr(special_chars, *src);
319 231629 int is_special =
320
6/6
✓ Branch 0 taken 231559 times.
✓ Branch 1 taken 70 times.
✓ Branch 2 taken 231509 times.
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 51 times.
✓ Branch 5 taken 231458 times.
231680 is_strictly_special || strchr("'\\", *src) ||
321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
51 (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE));
322
323
2/2
✓ Branch 0 taken 231559 times.
✓ Branch 1 taken 70 times.
231629 if (is_strictly_special ||
324
3/4
✓ Branch 0 taken 231559 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 231509 times.
✓ Branch 3 taken 50 times.
231559 (!(flags & AV_ESCAPE_FLAG_STRICT) &&
325
3/4
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 231458 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 51 times.
231509 (is_special || (is_ws && is_first_last))))
326 120 av_bprint_chars(dstbuf, '\\', 1);
327 231629 av_bprint_chars(dstbuf, *src, 1);
328 }
329 9476 break;
330 }
331 10769 }
332