blob: 2f059c5ba6bfd66e6d9fd98d02394fa226b92776
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\r" |
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 |