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 "compat/va_copy.h" | ||
29 | #include "error.h" | ||
30 | #include "macros.h" | ||
31 | #include "mem.h" | ||
32 | |||
33 | #define av_bprint_room(buf) ((buf)->size - FFMIN((buf)->len, (buf)->size)) | ||
34 | #define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer) | ||
35 | |||
36 | 1280 | static int av_bprint_alloc(AVBPrint *buf, unsigned room) | |
37 | { | ||
38 | char *old_str, *new_str; | ||
39 | unsigned min_size, new_size; | ||
40 | |||
41 |
2/2✓ Branch 0 taken 1147 times.
✓ Branch 1 taken 133 times.
|
1280 | if (buf->size == buf->size_max) |
42 | 1147 | return AVERROR(EIO); | |
43 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 133 times.
|
133 | if (!av_bprint_is_complete(buf)) |
44 | ✗ | return AVERROR_INVALIDDATA; /* it is already truncated anyway */ | |
45 | 133 | min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room); | |
46 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 132 times.
|
133 | new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2; |
47 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 125 times.
|
133 | if (new_size < min_size) |
48 | 8 | new_size = FFMIN(buf->size_max, min_size); | |
49 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 36 times.
|
133 | old_str = av_bprint_is_allocated(buf) ? buf->str : NULL; |
50 | 133 | new_str = av_realloc(old_str, new_size); | |
51 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 133 times.
|
133 | if (!new_str) |
52 | ✗ | return AVERROR(ENOMEM); | |
53 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 97 times.
|
133 | if (!old_str) |
54 | 36 | memcpy(new_str, buf->str, buf->len + 1); | |
55 | 133 | buf->str = new_str; | |
56 | 133 | buf->size = new_size; | |
57 | 133 | return 0; | |
58 | } | ||
59 | |||
60 | 2083619 | static void av_bprint_grow(AVBPrint *buf, unsigned extra_len) | |
61 | { | ||
62 | /* arbitrary margin to avoid small overflows */ | ||
63 | 2083619 | extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len); | |
64 | 2083619 | buf->len += extra_len; | |
65 |
2/2✓ Branch 0 taken 2083154 times.
✓ Branch 1 taken 465 times.
|
2083619 | if (buf->size) |
66 | 2083154 | buf->str[FFMIN(buf->len, buf->size - 1)] = 0; | |
67 | 2083619 | } | |
68 | |||
69 | 1742791 | void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max) | |
70 | { | ||
71 | 1742791 | unsigned size_auto = (char *)buf + sizeof(*buf) - | |
72 | buf->reserved_internal_buffer; | ||
73 | |||
74 |
2/2✓ Branch 0 taken 1260107 times.
✓ Branch 1 taken 482684 times.
|
1742791 | if (size_max == AV_BPRINT_SIZE_AUTOMATIC) |
75 | 1260107 | size_max = size_auto; | |
76 | 1742791 | buf->str = buf->reserved_internal_buffer; | |
77 | 1742791 | buf->len = 0; | |
78 | 1742791 | buf->size = FFMIN(size_auto, size_max); | |
79 | 1742791 | buf->size_max = size_max; | |
80 | 1742791 | *buf->str = 0; | |
81 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1742780 times.
|
1742791 | if (size_init > buf->size) |
82 | 11 | av_bprint_alloc(buf, size_init - 1); | |
83 | 1742791 | } | |
84 | |||
85 | 27086 | void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size) | |
86 | { | ||
87 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 27054 times.
|
27086 | if (size == 0) { |
88 | 32 | av_bprint_init(buf, 0, AV_BPRINT_SIZE_COUNT_ONLY); | |
89 | 32 | return; | |
90 | } | ||
91 | |||
92 | 27054 | buf->str = buffer; | |
93 | 27054 | buf->len = 0; | |
94 | 27054 | buf->size = size; | |
95 | 27054 | buf->size_max = size; | |
96 | 27054 | *buf->str = 0; | |
97 | } | ||
98 | |||
99 | 997021 | void av_bprintf(AVBPrint *buf, const char *fmt, ...) | |
100 | { | ||
101 | unsigned room; | ||
102 | char *dst; | ||
103 | va_list vl; | ||
104 | int extra_len; | ||
105 | |||
106 | while (1) { | ||
107 | 997033 | room = av_bprint_room(buf); | |
108 |
2/2✓ Branch 0 taken 995894 times.
✓ Branch 1 taken 1139 times.
|
997033 | dst = room ? buf->str + buf->len : NULL; |
109 | 997033 | va_start(vl, fmt); | |
110 | 997033 | extra_len = vsnprintf(dst, room, fmt, vl); | |
111 | 997033 | va_end(vl); | |
112 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 997025 times.
|
997033 | if (extra_len <= 0) |
113 | 8 | return; | |
114 |
2/2✓ Branch 0 taken 995870 times.
✓ Branch 1 taken 1155 times.
|
997025 | if (extra_len < room) |
115 | 995870 | break; | |
116 |
2/2✓ Branch 1 taken 1143 times.
✓ Branch 2 taken 12 times.
|
1155 | if (av_bprint_alloc(buf, extra_len)) |
117 | 1143 | break; | |
118 | } | ||
119 | 997013 | av_bprint_grow(buf, extra_len); | |
120 | } | ||
121 | |||
122 | 421134 | void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg) | |
123 | { | ||
124 | unsigned room; | ||
125 | char *dst; | ||
126 | int extra_len; | ||
127 | va_list vl; | ||
128 | |||
129 | while (1) { | ||
130 | 421134 | room = av_bprint_room(buf); | |
131 |
1/2✓ Branch 0 taken 421134 times.
✗ Branch 1 not taken.
|
421134 | dst = room ? buf->str + buf->len : NULL; |
132 | 421134 | va_copy(vl, vl_arg); | |
133 | 421134 | extra_len = vsnprintf(dst, room, fmt, vl); | |
134 | 421134 | va_end(vl); | |
135 |
2/2✓ Branch 0 taken 13935 times.
✓ Branch 1 taken 407199 times.
|
421134 | if (extra_len <= 0) |
136 | 13935 | return; | |
137 |
1/2✓ Branch 0 taken 407199 times.
✗ Branch 1 not taken.
|
407199 | if (extra_len < room) |
138 | 407199 | break; | |
139 | ✗ | if (av_bprint_alloc(buf, extra_len)) | |
140 | ✗ | break; | |
141 | } | ||
142 | 407199 | av_bprint_grow(buf, extra_len); | |
143 | } | ||
144 | |||
145 | 568916 | void av_bprint_chars(AVBPrint *buf, char c, unsigned n) | |
146 | { | ||
147 | unsigned room, real_n; | ||
148 | |||
149 | while (1) { | ||
150 | 568916 | room = av_bprint_room(buf); | |
151 |
2/2✓ Branch 0 taken 568913 times.
✓ Branch 1 taken 3 times.
|
568916 | if (n < room) |
152 | 568913 | break; | |
153 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | if (av_bprint_alloc(buf, n)) |
154 | 3 | break; | |
155 | } | ||
156 |
2/2✓ Branch 0 taken 568913 times.
✓ Branch 1 taken 3 times.
|
568916 | if (room) { |
157 | 568913 | real_n = FFMIN(n, room - 1); | |
158 | 568913 | memset(buf->str + buf->len, c, real_n); | |
159 | } | ||
160 | 568916 | av_bprint_grow(buf, n); | |
161 | 568916 | } | |
162 | |||
163 | 110490 | void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size) | |
164 | { | ||
165 | unsigned room, real_n; | ||
166 | |||
167 | while (1) { | ||
168 | 110588 | room = av_bprint_room(buf); | |
169 |
2/2✓ Branch 0 taken 110490 times.
✓ Branch 1 taken 98 times.
|
110588 | if (size < room) |
170 | 110490 | break; | |
171 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
|
98 | if (av_bprint_alloc(buf, size)) |
172 | ✗ | break; | |
173 | } | ||
174 |
1/2✓ Branch 0 taken 110490 times.
✗ Branch 1 not taken.
|
110490 | if (room) { |
175 | 110490 | real_n = FFMIN(size, room - 1); | |
176 | 110490 | memcpy(buf->str + buf->len, data, real_n); | |
177 | } | ||
178 | 110490 | av_bprint_grow(buf, size); | |
179 | 110490 | } | |
180 | |||
181 | 2 | void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm) | |
182 | { | ||
183 | unsigned room; | ||
184 | size_t l; | ||
185 | |||
186 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!*fmt) |
187 | ✗ | return; | |
188 | while (1) { | ||
189 | 2 | room = av_bprint_room(buf); | |
190 |
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))) |
191 | 1 | break; | |
192 | /* strftime does not tell us how much room it would need: let us | ||
193 | retry with twice as much until the buffer is large enough */ | ||
194 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | room = !room ? strlen(fmt) + 1 : |
195 | room <= INT_MAX / 2 ? room * 2 : INT_MAX; | ||
196 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (av_bprint_alloc(buf, room)) { |
197 | /* impossible to grow, try to manage something useful anyway */ | ||
198 | 1 | room = av_bprint_room(buf); | |
199 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (room < 1024) { |
200 | /* if strftime fails because the buffer has (almost) reached | ||
201 | its maximum size, let us try in a local buffer; 1k should | ||
202 | be enough to format any real date+time string */ | ||
203 | char buf2[1024]; | ||
204 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) { |
205 | 1 | av_bprintf(buf, "%s", buf2); | |
206 | 1 | return; | |
207 | } | ||
208 | } | ||
209 | ✗ | if (room) { | |
210 | /* if anything else failed and the buffer is not already | ||
211 | truncated, let us add a stock string and force truncation */ | ||
212 | static const char txt[] = "[truncated strftime output]"; | ||
213 | ✗ | memset(buf->str + buf->len, '!', room); | |
214 | ✗ | memcpy(buf->str + buf->len, txt, FFMIN(sizeof(txt) - 1, room)); | |
215 | ✗ | av_bprint_grow(buf, room); /* force truncation */ | |
216 | } | ||
217 | ✗ | return; | |
218 | } | ||
219 | } | ||
220 | 1 | av_bprint_grow(buf, l); | |
221 | } | ||
222 | |||
223 | 18 | void av_bprint_get_buffer(AVBPrint *buf, unsigned size, | |
224 | unsigned char **mem, unsigned *actual_size) | ||
225 | { | ||
226 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | if (size > av_bprint_room(buf)) |
227 | 12 | av_bprint_alloc(buf, size); | |
228 | 18 | *actual_size = av_bprint_room(buf); | |
229 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | *mem = *actual_size ? buf->str + buf->len : NULL; |
230 | 18 | } | |
231 | |||
232 | 33112 | void av_bprint_clear(AVBPrint *buf) | |
233 | { | ||
234 |
2/2✓ Branch 0 taken 12425 times.
✓ Branch 1 taken 20687 times.
|
33112 | if (buf->len) { |
235 | 12425 | *buf->str = 0; | |
236 | 12425 | buf->len = 0; | |
237 | } | ||
238 | 33112 | } | |
239 | |||
240 | 488367 | int av_bprint_finalize(AVBPrint *buf, char **ret_str) | |
241 | { | ||
242 | 488367 | unsigned real_size = FFMIN(buf->len + 1, buf->size); | |
243 | char *str; | ||
244 | 488367 | int ret = 0; | |
245 | |||
246 |
2/2✓ Branch 0 taken 10365 times.
✓ Branch 1 taken 478002 times.
|
488367 | if (ret_str) { |
247 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 10331 times.
|
10365 | if (av_bprint_is_allocated(buf)) { |
248 | 34 | str = av_realloc(buf->str, real_size); | |
249 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
|
34 | if (!str) |
250 | ✗ | str = buf->str; | |
251 | 34 | buf->str = NULL; | |
252 | } else { | ||
253 | 10331 | str = av_memdup(buf->str, real_size); | |
254 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10331 times.
|
10331 | if (!str) |
255 | ✗ | ret = AVERROR(ENOMEM); | |
256 | } | ||
257 | 10365 | *ret_str = str; | |
258 | } else { | ||
259 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 477997 times.
|
478002 | if (av_bprint_is_allocated(buf)) |
260 | 5 | av_freep(&buf->str); | |
261 | } | ||
262 | 488367 | buf->size = real_size; | |
263 | 488367 | return ret; | |
264 | } | ||
265 | |||
266 | #define WHITESPACES " \n\t\r" | ||
267 | |||
268 | 9283 | void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, | |
269 | enum AVEscapeMode mode, int flags) | ||
270 | { | ||
271 | 9283 | const char *src0 = src; | |
272 | |||
273 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9283 times.
|
9283 | if (mode == AV_ESCAPE_MODE_AUTO) |
274 | ✗ | mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */ | |
275 | |||
276 |
2/3✗ Branch 0 not taken.
✓ Branch 1 taken 1007 times.
✓ Branch 2 taken 8276 times.
|
9283 | switch (mode) { |
277 | ✗ | case AV_ESCAPE_MODE_QUOTE: | |
278 | /* enclose the string between '' */ | ||
279 | ✗ | av_bprint_chars(dstbuf, '\'', 1); | |
280 | ✗ | for (; *src; src++) { | |
281 | ✗ | if (*src == '\'') | |
282 | ✗ | av_bprintf(dstbuf, "'\\''"); | |
283 | else | ||
284 | ✗ | av_bprint_chars(dstbuf, *src, 1); | |
285 | } | ||
286 | ✗ | av_bprint_chars(dstbuf, '\'', 1); | |
287 | ✗ | break; | |
288 | |||
289 | 1007 | case AV_ESCAPE_MODE_XML: | |
290 | /* escape XML non-markup character data as per 2.4 by default: */ | ||
291 | /* [^<&]* - ([^<&]* ']]>' [^<&]*) */ | ||
292 | |||
293 | /* additionally, given one of the AV_ESCAPE_FLAG_XML_* flags, */ | ||
294 | /* escape those specific characters as required. */ | ||
295 |
2/2✓ Branch 0 taken 14203 times.
✓ Branch 1 taken 1007 times.
|
15210 | for (; *src; src++) { |
296 |
6/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14153 times.
|
14203 | switch (*src) { |
297 | 2 | case '&' : av_bprintf(dstbuf, "%s", "&"); break; | |
298 | 11 | case '<' : av_bprintf(dstbuf, "%s", "<"); break; | |
299 | 8 | case '>' : av_bprintf(dstbuf, "%s", ">"); break; | |
300 | 25 | case '\'': | |
301 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES)) |
302 | 25 | goto XML_DEFAULT_HANDLING; | |
303 | |||
304 | ✗ | av_bprintf(dstbuf, "%s", "'"); | |
305 | ✗ | break; | |
306 | 4 | case '"' : | |
307 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES)) |
308 | ✗ | goto XML_DEFAULT_HANDLING; | |
309 | |||
310 | 4 | av_bprintf(dstbuf, "%s", """); | |
311 | 4 | break; | |
312 | 25 | XML_DEFAULT_HANDLING: | |
313 | 14178 | default: av_bprint_chars(dstbuf, *src, 1); | |
314 | } | ||
315 | } | ||
316 | 1007 | break; | |
317 | |||
318 | /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */ | ||
319 | 8276 | default: | |
320 | /* \-escape characters */ | ||
321 |
2/2✓ Branch 0 taken 202517 times.
✓ Branch 1 taken 8276 times.
|
210793 | for (; *src; src++) { |
322 |
4/4✓ Branch 0 taken 194250 times.
✓ Branch 1 taken 8267 times.
✓ Branch 2 taken 8261 times.
✓ Branch 3 taken 185989 times.
|
202517 | int is_first_last = src == src0 || !*(src+1); |
323 | 202517 | int is_ws = !!strchr(WHITESPACES, *src); | |
324 |
3/4✓ Branch 0 taken 202517 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
✓ Branch 3 taken 202449 times.
|
202517 | int is_strictly_special = special_chars && strchr(special_chars, *src); |
325 | 202517 | int is_special = | |
326 |
5/6✓ Branch 0 taken 202449 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 202399 times.
✓ Branch 3 taken 50 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 202399 times.
|
202517 | is_strictly_special || strchr("'\\", *src) || |
327 | ✗ | (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE)); | |
328 | |||
329 |
2/2✓ Branch 0 taken 202449 times.
✓ Branch 1 taken 68 times.
|
202517 | if (is_strictly_special || |
330 |
3/4✓ Branch 0 taken 202449 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 202399 times.
✓ Branch 3 taken 50 times.
|
202449 | (!(flags & AV_ESCAPE_FLAG_STRICT) && |
331 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 202399 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
202399 | (is_special || (is_ws && is_first_last)))) |
332 | 118 | av_bprint_chars(dstbuf, '\\', 1); | |
333 | 202517 | av_bprint_chars(dstbuf, *src, 1); | |
334 | } | ||
335 | 8276 | break; | |
336 | } | ||
337 | 9283 | } | |
338 |