bprint.c (10265B)
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 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 == AV_BPRINT_SIZE_AUTOMATIC) 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 if (size == 0) { 88 av_bprint_init(buf, 0, AV_BPRINT_SIZE_COUNT_ONLY); 89 return; 90 } 91 92 buf->str = buffer; 93 buf->len = 0; 94 buf->size = size; 95 buf->size_max = size; 96 *buf->str = 0; 97 } 98 99 void av_bprintf(AVBPrint *buf, const char *fmt, ...) 100 { 101 unsigned room; 102 char *dst; 103 va_list vl; 104 int extra_len; 105 106 while (1) { 107 room = av_bprint_room(buf); 108 dst = room ? buf->str + buf->len : NULL; 109 va_start(vl, fmt); 110 extra_len = vsnprintf(dst, room, fmt, vl); 111 va_end(vl); 112 if (extra_len <= 0) 113 return; 114 if (extra_len < room) 115 break; 116 if (av_bprint_alloc(buf, extra_len)) 117 break; 118 } 119 av_bprint_grow(buf, extra_len); 120 } 121 122 void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg) 123 { 124 unsigned room; 125 char *dst; 126 int extra_len; 127 va_list vl; 128 129 while (1) { 130 room = av_bprint_room(buf); 131 dst = room ? buf->str + buf->len : NULL; 132 va_copy(vl, vl_arg); 133 extra_len = vsnprintf(dst, room, fmt, vl); 134 va_end(vl); 135 if (extra_len <= 0) 136 return; 137 if (extra_len < room) 138 break; 139 if (av_bprint_alloc(buf, extra_len)) 140 break; 141 } 142 av_bprint_grow(buf, extra_len); 143 } 144 145 void av_bprint_chars(AVBPrint *buf, char c, unsigned n) 146 { 147 unsigned room, real_n; 148 149 while (1) { 150 room = av_bprint_room(buf); 151 if (n < room) 152 break; 153 if (av_bprint_alloc(buf, n)) 154 break; 155 } 156 if (room) { 157 real_n = FFMIN(n, room - 1); 158 memset(buf->str + buf->len, c, real_n); 159 } 160 av_bprint_grow(buf, n); 161 } 162 163 void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size) 164 { 165 unsigned room, real_n; 166 167 while (1) { 168 room = av_bprint_room(buf); 169 if (size < room) 170 break; 171 if (av_bprint_alloc(buf, size)) 172 break; 173 } 174 if (room) { 175 real_n = FFMIN(size, room - 1); 176 memcpy(buf->str + buf->len, data, real_n); 177 } 178 av_bprint_grow(buf, size); 179 } 180 181 void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm) 182 { 183 unsigned room; 184 size_t l; 185 186 if (!*fmt) 187 return; 188 while (1) { 189 room = av_bprint_room(buf); 190 if (room && (l = strftime(buf->str + buf->len, room, fmt, tm))) 191 break; 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); 199 if (room < 1024) { 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 */ 203 char buf2[1024]; 204 if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) { 205 av_bprintf(buf, "%s", buf2); 206 return; 207 } 208 } 209 if (room) { 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 */ 216 } 217 return; 218 } 219 } 220 av_bprint_grow(buf, l); 221 } 222 223 void av_bprint_get_buffer(AVBPrint *buf, unsigned size, 224 unsigned char **mem, unsigned *actual_size) 225 { 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; 230 } 231 232 void av_bprint_clear(AVBPrint *buf) 233 { 234 if (buf->len) { 235 *buf->str = 0; 236 buf->len = 0; 237 } 238 } 239 240 int av_bprint_finalize(AVBPrint *buf, char **ret_str) 241 { 242 unsigned real_size = FFMIN(buf->len + 1, buf->size); 243 char *str; 244 int ret = 0; 245 246 if (ret_str) { 247 if (av_bprint_is_allocated(buf)) { 248 str = av_realloc(buf->str, real_size); 249 if (!str) 250 str = buf->str; 251 buf->str = NULL; 252 } else { 253 str = av_memdup(buf->str, real_size); 254 if (!str) 255 ret = AVERROR(ENOMEM); 256 } 257 *ret_str = str; 258 } else { 259 if (av_bprint_is_allocated(buf)) 260 av_freep(&buf->str); 261 } 262 buf->size = real_size; 263 return ret; 264 } 265 266 #define WHITESPACES " \n\t\r" 267 268 void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, 269 enum AVEscapeMode mode, int flags) 270 { 271 const char *src0 = src; 272 273 if (mode == AV_ESCAPE_MODE_AUTO) 274 mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */ 275 276 switch (mode) { 277 case AV_ESCAPE_MODE_QUOTE: 278 /* enclose the string between '' */ 279 av_bprint_chars(dstbuf, '\'', 1); 280 for (; *src; src++) { 281 if (*src == '\'') 282 av_bprintf(dstbuf, "'\\''"); 283 else 284 av_bprint_chars(dstbuf, *src, 1); 285 } 286 av_bprint_chars(dstbuf, '\'', 1); 287 break; 288 289 case AV_ESCAPE_MODE_XML: 290 /* escape XML non-markup character data as per 2.4 by default: */ 291 /* [^<&]* - ([^<&]* ']]>' [^<&]*) */ 292 293 /* additionally, given one of the AV_ESCAPE_FLAG_XML_* flags, */ 294 /* escape those specific characters as required. */ 295 for (; *src; src++) { 296 switch (*src) { 297 case '&' : av_bprintf(dstbuf, "%s", "&"); break; 298 case '<' : av_bprintf(dstbuf, "%s", "<"); break; 299 case '>' : av_bprintf(dstbuf, "%s", ">"); break; 300 case '\'': 301 if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES)) 302 goto XML_DEFAULT_HANDLING; 303 304 av_bprintf(dstbuf, "%s", "'"); 305 break; 306 case '"' : 307 if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES)) 308 goto XML_DEFAULT_HANDLING; 309 310 av_bprintf(dstbuf, "%s", """); 311 break; 312 XML_DEFAULT_HANDLING: 313 default: av_bprint_chars(dstbuf, *src, 1); 314 } 315 } 316 break; 317 318 /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */ 319 default: 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); 325 int is_special = 326 is_strictly_special || strchr("'\\", *src) || 327 (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE)); 328 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); 334 } 335 break; 336 } 337 }