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