Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
52868 views
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
382
383