Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 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 |
||
22 | #include |
||
23 | #include |
||
24 | #include |
||
25 | #include "avassert.h" |
||
26 | #include "avstring.h" |
||
27 | #include "bprint.h" |
||
28 | #include "common.h" |
||
29 | #include "compat/va_copy.h" |
||
30 | #include "error.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 | static int av_bprint_alloc(AVBPrint *buf, unsigned room) |
||
37 | { |
||
38 | char *old_str, *new_str; |
||
39 | unsigned min_size, new_size; |
||
40 | |||
41 | if (buf->size == buf->size_max) |
||
42 | return AVERROR(EIO); |
||
43 | if (!av_bprint_is_complete(buf)) |
||
44 | return AVERROR_INVALIDDATA; /* it is already truncated anyway */ |
||
45 | min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room); |
||
46 | new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2; |
||
47 | if (new_size < min_size) |
||
48 | new_size = FFMIN(buf->size_max, min_size); |
||
49 | old_str = av_bprint_is_allocated(buf) ? buf->str : NULL; |
||
50 | new_str = av_realloc(old_str, new_size); |
||
51 | if (!new_str) |
||
52 | return AVERROR(ENOMEM); |
||
53 | if (!old_str) |
||
54 | memcpy(new_str, buf->str, buf->len + 1); |
||
55 | buf->str = new_str; |
||
56 | buf->size = new_size; |
||
57 | return 0; |
||
58 | } |
||
59 | |||
60 | static void av_bprint_grow(AVBPrint *buf, unsigned extra_len) |
||
61 | { |
||
62 | /* arbitrary margin to avoid small overflows */ |
||
63 | extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len); |
||
64 | buf->len += extra_len; |
||
65 | if (buf->size) |
||
66 | buf->str[FFMIN(buf->len, buf->size - 1)] = 0; |
||
67 | } |
||
68 | |||
69 | void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max) |
||
70 | { |
||
71 | unsigned size_auto = (char *)buf + sizeof(*buf) - |
||
72 | buf->reserved_internal_buffer; |
||
73 | |||
74 | if (size_max == 1) |
||
75 | size_max = size_auto; |
||
76 | buf->str = buf->reserved_internal_buffer; |
||
77 | buf->len = 0; |
||
78 | buf->size = FFMIN(size_auto, size_max); |
||
79 | buf->size_max = size_max; |
||
80 | *buf->str = 0; |
||
81 | if (size_init > buf->size) |
||
82 | av_bprint_alloc(buf, size_init - 1); |
||
83 | } |
||
84 | |||
85 | void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size) |
||
86 | { |
||
87 | buf->str = buffer; |
||
88 | buf->len = 0; |
||
89 | buf->size = size; |
||
90 | buf->size_max = size; |
||
91 | *buf->str = 0; |
||
92 | } |
||
93 | |||
94 | void av_bprintf(AVBPrint *buf, const char *fmt, ...) |
||
95 | { |
||
96 | unsigned room; |
||
97 | char *dst; |
||
98 | va_list vl; |
||
99 | int extra_len; |
||
100 | |||
101 | while (1) { |
||
102 | room = av_bprint_room(buf); |
||
103 | dst = room ? buf->str + buf->len : NULL; |
||
104 | va_start(vl, fmt); |
||
105 | extra_len = vsnprintf(dst, room, fmt, vl); |
||
106 | va_end(vl); |
||
107 | if (extra_len <= 0) |
||
108 | return; |
||
109 | if (extra_len < room) |
||
110 | break; |
||
111 | if (av_bprint_alloc(buf, extra_len)) |
||
112 | break; |
||
113 | } |
||
114 | av_bprint_grow(buf, extra_len); |
||
115 | } |
||
116 | |||
117 | void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg) |
||
118 | { |
||
119 | unsigned room; |
||
120 | char *dst; |
||
121 | int extra_len; |
||
122 | va_list vl; |
||
123 | |||
124 | while (1) { |
||
125 | room = av_bprint_room(buf); |
||
126 | dst = room ? buf->str + buf->len : NULL; |
||
127 | va_copy(vl, vl_arg); |
||
128 | extra_len = vsnprintf(dst, room, fmt, vl); |
||
129 | va_end(vl); |
||
130 | if (extra_len <= 0) |
||
131 | return; |
||
132 | if (extra_len < room) |
||
133 | break; |
||
134 | if (av_bprint_alloc(buf, extra_len)) |
||
135 | break; |
||
136 | } |
||
137 | av_bprint_grow(buf, extra_len); |
||
138 | } |
||
139 | |||
140 | void av_bprint_chars(AVBPrint *buf, char c, unsigned n) |
||
141 | { |
||
142 | unsigned room, real_n; |
||
143 | |||
144 | while (1) { |
||
145 | room = av_bprint_room(buf); |
||
146 | if (n < room) |
||
147 | break; |
||
148 | if (av_bprint_alloc(buf, n)) |
||
149 | break; |
||
150 | } |
||
151 | if (room) { |
||
152 | real_n = FFMIN(n, room - 1); |
||
153 | memset(buf->str + buf->len, c, real_n); |
||
154 | } |
||
155 | av_bprint_grow(buf, n); |
||
156 | } |
||
157 | |||
158 | void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size) |
||
159 | { |
||
160 | unsigned room, real_n; |
||
161 | |||
162 | while (1) { |
||
163 | room = av_bprint_room(buf); |
||
164 | if (size < room) |
||
165 | break; |
||
166 | if (av_bprint_alloc(buf, size)) |
||
167 | break; |
||
168 | } |
||
169 | if (room) { |
||
170 | real_n = FFMIN(size, room - 1); |
||
171 | memcpy(buf->str + buf->len, data, real_n); |
||
172 | } |
||
173 | av_bprint_grow(buf, size); |
||
174 | } |
||
175 | |||
176 | void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm) |
||
177 | { |
||
178 | unsigned room; |
||
179 | size_t l; |
||
180 | |||
181 | if (!*fmt) |
||
182 | return; |
||
183 | while (1) { |
||
184 | room = av_bprint_room(buf); |
||
185 | if (room && (l = strftime(buf->str + buf->len, room, fmt, tm))) |
||
186 | break; |
||
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 | room = !room ? strlen(fmt) + 1 : |
||
190 | room <= INT_MAX / 2 ? room * 2 : INT_MAX; |
||
191 | if (av_bprint_alloc(buf, room)) { |
||
192 | /* impossible to grow, try to manage something useful anyway */ |
||
193 | room = av_bprint_room(buf); |
||
194 | 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 | if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) { |
||
200 | av_bprintf(buf, "%s", buf2); |
||
201 | 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 | av_bprint_grow(buf, l); |
||
216 | } |
||
217 | |||
218 | void av_bprint_get_buffer(AVBPrint *buf, unsigned size, |
||
219 | unsigned char **mem, unsigned *actual_size) |
||
220 | { |
||
221 | if (size > av_bprint_room(buf)) |
||
222 | av_bprint_alloc(buf, size); |
||
223 | *actual_size = av_bprint_room(buf); |
||
224 | *mem = *actual_size ? buf->str + buf->len : NULL; |
||
225 | } |
||
226 | |||
227 | void av_bprint_clear(AVBPrint *buf) |
||
228 | { |
||
229 | if (buf->len) { |
||
230 | *buf->str = 0; |
||
231 | buf->len = 0; |
||
232 | } |
||
233 | } |
||
234 | |||
235 | int av_bprint_finalize(AVBPrint *buf, char **ret_str) |
||
236 | { |
||
237 | unsigned real_size = FFMIN(buf->len + 1, buf->size); |
||
238 | char *str; |
||
239 | int ret = 0; |
||
240 | |||
241 | if (ret_str) { |
||
242 | if (av_bprint_is_allocated(buf)) { |
||
243 | str = av_realloc(buf->str, real_size); |
||
244 | if (!str) |
||
245 | str = buf->str; |
||
246 | buf->str = NULL; |
||
247 | } else { |
||
248 | str = av_malloc(real_size); |
||
249 | if (str) |
||
250 | memcpy(str, buf->str, real_size); |
||
251 | else |
||
252 | ret = AVERROR(ENOMEM); |
||
253 | } |
||
254 | *ret_str = str; |
||
255 | } else { |
||
256 | if (av_bprint_is_allocated(buf)) |
||
257 | av_freep(&buf->str); |
||
258 | } |
||
259 | buf->size = real_size; |
||
260 | return ret; |
||
261 | } |
||
262 | |||
263 | #define WHITESPACES " \n\t" |
||
264 | |||
265 | void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, |
||
266 | enum AVEscapeMode mode, int flags) |
||
267 | { |
||
268 | const char *src0 = src; |
||
269 | |||
270 | if (mode == AV_ESCAPE_MODE_AUTO) |
||
271 | mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */ |
||
272 | |||
273 | switch (mode) { |
||
274 | case AV_ESCAPE_MODE_QUOTE: |
||
275 | /* enclose the string between '' */ |
||
276 | av_bprint_chars(dstbuf, '\'', 1); |
||
277 | for (; *src; src++) { |
||
278 | if (*src == '\'') |
||
279 | av_bprintf(dstbuf, "'\\''"); |
||
280 | else |
||
281 | av_bprint_chars(dstbuf, *src, 1); |
||
282 | } |
||
283 | av_bprint_chars(dstbuf, '\'', 1); |
||
284 | break; |
||
285 | |||
286 | /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */ |
||
287 | default: |
||
288 | /* \-escape characters */ |
||
289 | for (; *src; src++) { |
||
290 | int is_first_last = src == src0 || !*(src+1); |
||
291 | int is_ws = !!strchr(WHITESPACES, *src); |
||
292 | int is_strictly_special = special_chars && strchr(special_chars, *src); |
||
293 | int is_special = |
||
294 | is_strictly_special || strchr("'\\", *src) || |
||
295 | (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE)); |
||
296 | |||
297 | if (is_strictly_special || |
||
298 | (!(flags & AV_ESCAPE_FLAG_STRICT) && |
||
299 | (is_special || (is_ws && is_first_last)))) |
||
300 | av_bprint_chars(dstbuf, '\\', 1); |
||
301 | av_bprint_chars(dstbuf, *src, 1); |
||
302 | } |
||
303 | break; |
||
304 | } |
||
305 | } |
||
306 | |||
307 | #ifdef TEST |
||
308 | |||
309 | #undef printf |
||
310 | |||
311 | static void bprint_pascal(AVBPrint *b, unsigned size) |
||
312 | { |
||
313 | unsigned i, j; |
||
314 | unsigned p[42]; |
||
315 | |||
316 | av_assert0(size < FF_ARRAY_ELEMS(p)); |
||
317 | |||
318 | p[0] = 1; |
||
319 | av_bprintf(b, "%8d\n", 1); |
||
320 | for (i = 1; i <= size; i++) { |
||
321 | p[i] = 1; |
||
322 | for (j = i - 1; j > 0; j--) |
||
323 | p[j] = p[j] + p[j - 1]; |
||
324 | for (j = 0; j <= i; j++) |
||
325 | av_bprintf(b, "%8d", p[j]); |
||
326 | av_bprintf(b, "\n"); |
||
327 | } |
||
328 | } |
||
329 | |||
330 | int main(void) |
||
331 | { |
||
332 | AVBPrint b; |
||
333 | char buf[256]; |
||
334 | struct tm testtime = { .tm_year = 100, .tm_mon = 11, .tm_mday = 20 }; |
||
335 | |||
336 | av_bprint_init(&b, 0, -1); |
||
337 | bprint_pascal(&b, 5); |
||
338 | printf("Short text in unlimited buffer: %u/%u\n", (unsigned)strlen(b.str), b.len); |
||
339 | printf("%s\n", b.str); |
||
340 | av_bprint_finalize(&b, NULL); |
||
341 | |||
342 | av_bprint_init(&b, 0, -1); |
||
343 | bprint_pascal(&b, 25); |
||
344 | printf("Long text in unlimited buffer: %u/%u\n", (unsigned)strlen(b.str), b.len); |
||
345 | av_bprint_finalize(&b, NULL); |
||
346 | |||
347 | av_bprint_init(&b, 0, 2048); |
||
348 | bprint_pascal(&b, 25); |
||
349 | printf("Long text in limited buffer: %u/%u\n", (unsigned)strlen(b.str), b.len); |
||
350 | av_bprint_finalize(&b, NULL); |
||
351 | |||
352 | av_bprint_init(&b, 0, 1); |
||
353 | bprint_pascal(&b, 5); |
||
354 | printf("Short text in automatic buffer: %u/%u\n", (unsigned)strlen(b.str), b.len); |
||
355 | |||
356 | av_bprint_init(&b, 0, 1); |
||
357 | bprint_pascal(&b, 25); |
||
358 | printf("Long text in automatic buffer: %u/%u\n", (unsigned)strlen(b.str)/8*8, b.len); |
||
359 | /* Note that the size of the automatic buffer is arch-dependant. */ |
||
360 | |||
361 | av_bprint_init(&b, 0, 0); |
||
362 | bprint_pascal(&b, 25); |
||
363 | printf("Long text count only buffer: %u/%u\n", (unsigned)strlen(b.str), b.len); |
||
364 | |||
365 | av_bprint_init_for_buffer(&b, buf, sizeof(buf)); |
||
366 | bprint_pascal(&b, 25); |
||
367 | printf("Long text count only buffer: %u/%u\n", (unsigned)strlen(buf), b.len); |
||
368 | |||
369 | av_bprint_init(&b, 0, -1); |
||
370 | av_bprint_strftime(&b, "%Y-%m-%d", &testtime); |
||
371 | printf("strftime full: %u/%u \"%s\"\n", (unsigned)strlen(buf), b.len, b.str); |
||
372 | av_bprint_finalize(&b, NULL); |
||
373 | |||
374 | av_bprint_init(&b, 0, 8); |
||
375 | av_bprint_strftime(&b, "%Y-%m-%d", &testtime); |
||
376 | printf("strftime truncated: %u/%u \"%s\"\n", (unsigned)strlen(buf), b.len, b.str); |
||
377 | |||
378 | return 0; |
||
379 | } |
||
380 | |||
381 | #endif=>=>>>=>>>>=>>=>> |