/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", "&"); break; |
292 | 0 | case '<' : av_bprintf(dstbuf, "%s", "<"); break; |
293 | 0 | case '>' : av_bprintf(dstbuf, "%s", ">"); 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", "'"); |
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", """); |
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 | } |