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 <limits.h> 22#include <stdarg.h> 23#include <stdio.h> 24#include <string.h> 25#include <time.h> 26#include "avstring.h" 27#include "bprint.h" 28#include "compat/va_copy.h" 29#include "error.h" 30#include "macros.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 36static 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 60static 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 69void 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 85void 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 94void 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 117void 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 140void 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 158void 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 176void 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 218void 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 227void av_bprint_clear(AVBPrint *buf) 228{ 229 if (buf->len) { 230 *buf->str = 0; 231 buf->len = 0; 232 } 233} 234 235int 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_memdup(buf->str, real_size); 249 if (!str) 250 ret = AVERROR(ENOMEM); 251 } 252 *ret_str = str; 253 } else { 254 if (av_bprint_is_allocated(buf)) 255 av_freep(&buf->str); 256 } 257 buf->size = real_size; 258 return ret; 259} 260 261#define WHITESPACES " \n\t\r" 262 263void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, 264 enum AVEscapeMode mode, int flags) 265{ 266 const char *src0 = src; 267 268 if (mode == AV_ESCAPE_MODE_AUTO) 269 mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */ 270 271 switch (mode) { 272 case AV_ESCAPE_MODE_QUOTE: 273 /* enclose the string between '' */ 274 av_bprint_chars(dstbuf, '\'', 1); 275 for (; *src; src++) { 276 if (*src == '\'') 277 av_bprintf(dstbuf, "'\\''"); 278 else 279 av_bprint_chars(dstbuf, *src, 1); 280 } 281 av_bprint_chars(dstbuf, '\'', 1); 282 break; 283 284 case AV_ESCAPE_MODE_XML: 285 /* escape XML non-markup character data as per 2.4 by default: */ 286 /* [^<&]* - ([^<&]* ']]>' [^<&]*) */ 287 288 /* additionally, given one of the AV_ESCAPE_FLAG_XML_* flags, */ 289 /* escape those specific characters as required. */ 290 for (; *src; src++) { 291 switch (*src) { 292 case '&' : av_bprintf(dstbuf, "%s", "&"); break; 293 case '<' : av_bprintf(dstbuf, "%s", "<"); break; 294 case '>' : av_bprintf(dstbuf, "%s", ">"); break; 295 case '\'': 296 if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES)) 297 goto XML_DEFAULT_HANDLING; 298 299 av_bprintf(dstbuf, "%s", "'"); 300 break; 301 case '"' : 302 if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES)) 303 goto XML_DEFAULT_HANDLING; 304 305 av_bprintf(dstbuf, "%s", """); 306 break; 307XML_DEFAULT_HANDLING: 308 default: av_bprint_chars(dstbuf, *src, 1); 309 } 310 } 311 break; 312 313 /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */ 314 default: 315 /* \-escape characters */ 316 for (; *src; src++) { 317 int is_first_last = src == src0 || !*(src+1); 318 int is_ws = !!strchr(WHITESPACES, *src); 319 int is_strictly_special = special_chars && strchr(special_chars, *src); 320 int is_special = 321 is_strictly_special || strchr("'\\", *src) || 322 (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE)); 323 324 if (is_strictly_special || 325 (!(flags & AV_ESCAPE_FLAG_STRICT) && 326 (is_special || (is_ws && is_first_last)))) 327 av_bprint_chars(dstbuf, '\\', 1); 328 av_bprint_chars(dstbuf, *src, 1); 329 } 330 break; 331 } 332} 333