2 * Copyright (c) 2012 Nicolas George
4 * This file is part of FFmpeg.
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.
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.
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
28 #include "compat/va_copy.h"
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)
36 static int av_bprint_alloc(AVBPrint
*buf
, unsigned room
)
38 char *old_str
, *new_str
;
39 unsigned min_size
, new_size
;
41 if (buf
->size
== buf
->size_max
)
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
);
52 return AVERROR(ENOMEM
);
54 memcpy(new_str
, buf
->str
, buf
->len
+ 1);
60 static void av_bprint_grow(AVBPrint
*buf
, unsigned extra_len
)
62 /* arbitrary margin to avoid small overflows */
63 extra_len
= FFMIN(extra_len
, UINT_MAX
- 5 - buf
->len
);
64 buf
->len
+= extra_len
;
66 buf
->str
[FFMIN(buf
->len
, buf
->size
- 1)] = 0;
69 void av_bprint_init(AVBPrint
*buf
, unsigned size_init
, unsigned size_max
)
71 unsigned size_auto
= (char *)buf
+ sizeof(*buf
) -
72 buf
->reserved_internal_buffer
;
74 if (size_max
== AV_BPRINT_SIZE_AUTOMATIC
)
76 buf
->str
= buf
->reserved_internal_buffer
;
78 buf
->size
= FFMIN(size_auto
, size_max
);
79 buf
->size_max
= size_max
;
81 if (size_init
> buf
->size
)
82 av_bprint_alloc(buf
, size_init
- 1);
85 void av_bprint_init_for_buffer(AVBPrint
*buf
, char *buffer
, unsigned size
)
88 av_bprint_init(buf
, 0, AV_BPRINT_SIZE_COUNT_ONLY
);
99 void av_bprintf(AVBPrint
*buf
, const char *fmt
, ...)
107 room
= av_bprint_room(buf
);
108 dst
= room
? buf
->str
+ buf
->len
: NULL
;
110 extra_len
= vsnprintf(dst
, room
, fmt
, vl
);
114 if (extra_len
< room
)
116 if (av_bprint_alloc(buf
, extra_len
))
119 av_bprint_grow(buf
, extra_len
);
122 void av_vbprintf(AVBPrint
*buf
, const char *fmt
, va_list vl_arg
)
130 room
= av_bprint_room(buf
);
131 dst
= room
? buf
->str
+ buf
->len
: NULL
;
133 extra_len
= vsnprintf(dst
, room
, fmt
, vl
);
137 if (extra_len
< room
)
139 if (av_bprint_alloc(buf
, extra_len
))
142 av_bprint_grow(buf
, extra_len
);
145 void av_bprint_chars(AVBPrint
*buf
, char c
, unsigned n
)
147 unsigned room
, real_n
;
150 room
= av_bprint_room(buf
);
153 if (av_bprint_alloc(buf
, n
))
157 real_n
= FFMIN(n
, room
- 1);
158 memset(buf
->str
+ buf
->len
, c
, real_n
);
160 av_bprint_grow(buf
, n
);
163 void av_bprint_append_data(AVBPrint
*buf
, const char *data
, unsigned size
)
165 unsigned room
, real_n
;
168 room
= av_bprint_room(buf
);
171 if (av_bprint_alloc(buf
, size
))
175 real_n
= FFMIN(size
, room
- 1);
176 memcpy(buf
->str
+ buf
->len
, data
, real_n
);
178 av_bprint_grow(buf
, size
);
181 void av_bprint_strftime(AVBPrint
*buf
, const char *fmt
, const struct tm
*tm
)
189 room
= av_bprint_room(buf
);
190 if (room
&& (l
= strftime(buf
->str
+ buf
->len
, room
, fmt
, tm
)))
192 /* strftime does not tell us how much room it would need: let us
193 retry with twice as much until the buffer is large enough */
194 room
= !room
? strlen(fmt
) + 1 :
195 room
<= INT_MAX
/ 2 ? room
* 2 : INT_MAX
;
196 if (av_bprint_alloc(buf
, room
)) {
197 /* impossible to grow, try to manage something useful anyway */
198 room
= av_bprint_room(buf
);
200 /* if strftime fails because the buffer has (almost) reached
201 its maximum size, let us try in a local buffer; 1k should
202 be enough to format any real date+time string */
204 if ((l
= strftime(buf2
, sizeof(buf2
), fmt
, tm
))) {
205 av_bprintf(buf
, "%s", buf2
);
210 /* if anything else failed and the buffer is not already
211 truncated, let us add a stock string and force truncation */
212 static const char txt
[] = "[truncated strftime output]";
213 memset(buf
->str
+ buf
->len
, '!', room
);
214 memcpy(buf
->str
+ buf
->len
, txt
, FFMIN(sizeof(txt
) - 1, room
));
215 av_bprint_grow(buf
, room
); /* force truncation */
220 av_bprint_grow(buf
, l
);
223 void av_bprint_get_buffer(AVBPrint
*buf
, unsigned size
,
224 unsigned char **mem
, unsigned *actual_size
)
226 if (size
> av_bprint_room(buf
))
227 av_bprint_alloc(buf
, size
);
228 *actual_size
= av_bprint_room(buf
);
229 *mem
= *actual_size
? buf
->str
+ buf
->len
: NULL
;
232 void av_bprint_clear(AVBPrint
*buf
)
240 int av_bprint_finalize(AVBPrint
*buf
, char **ret_str
)
242 unsigned real_size
= FFMIN(buf
->len
+ 1, buf
->size
);
247 if (av_bprint_is_allocated(buf
)) {
248 str
= av_realloc(buf
->str
, real_size
);
253 str
= av_memdup(buf
->str
, real_size
);
255 ret
= AVERROR(ENOMEM
);
259 if (av_bprint_is_allocated(buf
))
262 buf
->size
= real_size
;
266 #define WHITESPACES " \n\t\r"
268 void av_bprint_escape(AVBPrint
*dstbuf
, const char *src
, const char *special_chars
,
269 enum AVEscapeMode mode
, int flags
)
271 const char *src0
= src
;
273 if (mode
== AV_ESCAPE_MODE_AUTO
)
274 mode
= AV_ESCAPE_MODE_BACKSLASH
; /* TODO: implement a heuristic */
277 case AV_ESCAPE_MODE_QUOTE
:
278 /* enclose the string between '' */
279 av_bprint_chars(dstbuf
, '\'', 1);
280 for (; *src
; src
++) {
282 av_bprintf(dstbuf
, "'\\''");
284 av_bprint_chars(dstbuf
, *src
, 1);
286 av_bprint_chars(dstbuf
, '\'', 1);
289 case AV_ESCAPE_MODE_XML
:
290 /* escape XML non-markup character data as per 2.4 by default: */
291 /* [^<&]* - ([^<&]* ']]>' [^<&]*) */
293 /* additionally, given one of the AV_ESCAPE_FLAG_XML_* flags, */
294 /* escape those specific characters as required. */
295 for (; *src
; src
++) {
297 case '&' : av_bprintf(dstbuf
, "%s", "&"); break;
298 case '<' : av_bprintf(dstbuf
, "%s", "<"); break;
299 case '>' : av_bprintf(dstbuf
, "%s", ">"); break;
301 if (!(flags
& AV_ESCAPE_FLAG_XML_SINGLE_QUOTES
))
302 goto XML_DEFAULT_HANDLING
;
304 av_bprintf(dstbuf
, "%s", "'");
307 if (!(flags
& AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES
))
308 goto XML_DEFAULT_HANDLING
;
310 av_bprintf(dstbuf
, "%s", """);
312 XML_DEFAULT_HANDLING
:
313 default: av_bprint_chars(dstbuf
, *src
, 1);
318 /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */
320 /* \-escape characters */
321 for (; *src
; src
++) {
322 int is_first_last
= src
== src0
|| !*(src
+1);
323 int is_ws
= !!strchr(WHITESPACES
, *src
);
324 int is_strictly_special
= special_chars
&& strchr(special_chars
, *src
);
326 is_strictly_special
|| strchr("'\\", *src
) ||
327 (is_ws
&& (flags
& AV_ESCAPE_FLAG_WHITESPACE
));
329 if (is_strictly_special
||
330 (!(flags
& AV_ESCAPE_FLAG_STRICT
) &&
331 (is_special
|| (is_ws
&& is_first_last
))))
332 av_bprint_chars(dstbuf
, '\\', 1);
333 av_bprint_chars(dstbuf
, *src
, 1);