Commit | Line | Data |
---|---|---|
2ba45a60 DM |
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 <stdarg.h> | |
22 | #include <stdio.h> | |
23 | #include <string.h> | |
24 | #include <time.h> | |
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-dependent. */ | |
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 |