| Line | Branch | Exec | Source |
|---|---|---|---|
| 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 "error.h" | ||
| 29 | #include "macros.h" | ||
| 30 | #include "mem.h" | ||
| 31 | |||
| 32 | #define av_bprint_room(buf) ((buf)->size - FFMIN((buf)->len, (buf)->size)) | ||
| 33 | #define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer) | ||
| 34 | |||
| 35 | 70619 | static int av_bprint_alloc(AVBPrint *buf, unsigned room) | |
| 36 | { | ||
| 37 | char *old_str, *new_str; | ||
| 38 | unsigned min_size, new_size; | ||
| 39 | |||
| 40 |
2/2✓ Branch 0 taken 70432 times.
✓ Branch 1 taken 187 times.
|
70619 | if (buf->size == buf->size_max) |
| 41 | 70432 | return AVERROR(EIO); | |
| 42 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
|
187 | if (!av_bprint_is_complete(buf)) |
| 43 | ✗ | return AVERROR_INVALIDDATA; /* it is already truncated anyway */ | |
| 44 | 187 | min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room); | |
| 45 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 186 times.
|
187 | new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2; |
| 46 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 159 times.
|
187 | if (new_size < min_size) |
| 47 | 28 | new_size = FFMIN(buf->size_max, min_size); | |
| 48 |
2/2✓ Branch 0 taken 105 times.
✓ Branch 1 taken 82 times.
|
187 | old_str = av_bprint_is_allocated(buf) ? buf->str : NULL; |
| 49 | 187 | new_str = av_realloc(old_str, new_size); | |
| 50 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 187 times.
|
187 | if (!new_str) |
| 51 | ✗ | return AVERROR(ENOMEM); | |
| 52 |
2/2✓ Branch 0 taken 82 times.
✓ Branch 1 taken 105 times.
|
187 | if (!old_str) |
| 53 | 82 | memcpy(new_str, buf->str, buf->len + 1); | |
| 54 | 187 | buf->str = new_str; | |
| 55 | 187 | buf->size = new_size; | |
| 56 | 187 | return 0; | |
| 57 | } | ||
| 58 | |||
| 59 | 286573983 | static void av_bprint_grow(AVBPrint *buf, unsigned extra_len) | |
| 60 | { | ||
| 61 | /* arbitrary margin to avoid small overflows */ | ||
| 62 | 286573983 | extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len); | |
| 63 | 286573983 | buf->len += extra_len; | |
| 64 |
2/2✓ Branch 0 taken 286504233 times.
✓ Branch 1 taken 69750 times.
|
286573983 | if (buf->size) |
| 65 | 286504233 | buf->str[FFMIN(buf->len, buf->size - 1)] = 0; | |
| 66 | 286573983 | } | |
| 67 | |||
| 68 | 3962510 | void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max) | |
| 69 | { | ||
| 70 | 3962510 | unsigned size_auto = (char *)buf + sizeof(*buf) - | |
| 71 | buf->reserved_internal_buffer; | ||
| 72 | |||
| 73 |
2/2✓ Branch 0 taken 3257475 times.
✓ Branch 1 taken 705035 times.
|
3962510 | if (size_max == AV_BPRINT_SIZE_AUTOMATIC) |
| 74 | 3257475 | size_max = size_auto; | |
| 75 | 3962510 | buf->str = buf->reserved_internal_buffer; | |
| 76 | 3962510 | buf->len = 0; | |
| 77 | 3962510 | buf->size = FFMIN(size_auto, size_max); | |
| 78 | 3962510 | buf->size_max = size_max; | |
| 79 | 3962510 | *buf->str = 0; | |
| 80 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 3962465 times.
|
3962510 | if (size_init > buf->size) |
| 81 | 45 | av_bprint_alloc(buf, size_init - 1); | |
| 82 | 3962510 | } | |
| 83 | |||
| 84 | 1173965 | void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size) | |
| 85 | { | ||
| 86 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1173933 times.
|
1173965 | if (size == 0) { |
| 87 | 32 | av_bprint_init(buf, 0, AV_BPRINT_SIZE_COUNT_ONLY); | |
| 88 | 32 | return; | |
| 89 | } | ||
| 90 | |||
| 91 | 1173933 | buf->str = buffer; | |
| 92 | 1173933 | buf->len = 0; | |
| 93 | 1173933 | buf->size = size; | |
| 94 | 1173933 | buf->size_max = size; | |
| 95 | 1173933 | *buf->str = 0; | |
| 96 | } | ||
| 97 | |||
| 98 | 274818889 | void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg) | |
| 99 | { | ||
| 100 | unsigned room; | ||
| 101 | char *dst; | ||
| 102 | int extra_len; | ||
| 103 | va_list vl; | ||
| 104 | |||
| 105 | while (1) { | ||
| 106 | 274818914 | room = av_bprint_room(buf); | |
| 107 |
2/2✓ Branch 0 taken 274817467 times.
✓ Branch 1 taken 1447 times.
|
274818914 | dst = room ? buf->str + buf->len : NULL; |
| 108 | 274818914 | va_copy(vl, vl_arg); | |
| 109 | 274818914 | extra_len = vsnprintf(dst, room, fmt, vl); | |
| 110 | 274818914 | va_end(vl); | |
| 111 |
2/2✓ Branch 0 taken 2526670 times.
✓ Branch 1 taken 272292244 times.
|
274818914 | if (extra_len <= 0) |
| 112 | 2526670 | return; | |
| 113 |
2/2✓ Branch 0 taken 272290768 times.
✓ Branch 1 taken 1476 times.
|
272292244 | if (extra_len < room) |
| 114 | 272290768 | break; | |
| 115 |
2/2✓ Branch 1 taken 1451 times.
✓ Branch 2 taken 25 times.
|
1476 | if (av_bprint_alloc(buf, extra_len)) |
| 116 | 1451 | break; | |
| 117 | } | ||
| 118 | 272292219 | av_bprint_grow(buf, extra_len); | |
| 119 | } | ||
| 120 | |||
| 121 | 274318752 | void av_bprintf(AVBPrint *buf, const char *fmt, ...) | |
| 122 | { | ||
| 123 | va_list vl; | ||
| 124 | 274318752 | va_start(vl, fmt); | |
| 125 | 274318752 | av_vbprintf(buf, fmt, vl); | |
| 126 | 274318752 | va_end(vl); | |
| 127 | 274318752 | } | |
| 128 | |||
| 129 | 14002184 | void av_bprint_chars(AVBPrint *buf, char c, unsigned n) | |
| 130 | { | ||
| 131 | unsigned room, real_n; | ||
| 132 | |||
| 133 | while (1) { | ||
| 134 | 14002184 | room = av_bprint_room(buf); | |
| 135 |
2/2✓ Branch 0 taken 13933204 times.
✓ Branch 1 taken 68980 times.
|
14002184 | if (n < room) |
| 136 | 13933204 | break; | |
| 137 |
1/2✓ Branch 1 taken 68980 times.
✗ Branch 2 not taken.
|
68980 | if (av_bprint_alloc(buf, n)) |
| 138 | 68980 | break; | |
| 139 | } | ||
| 140 |
2/2✓ Branch 0 taken 13933204 times.
✓ Branch 1 taken 68980 times.
|
14002184 | if (room) { |
| 141 | 13933204 | real_n = FFMIN(n, room - 1); | |
| 142 | 13933204 | memset(buf->str + buf->len, c, real_n); | |
| 143 | } | ||
| 144 | 14002184 | av_bprint_grow(buf, n); | |
| 145 | 14002184 | } | |
| 146 | |||
| 147 | 279579 | void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size) | |
| 148 | { | ||
| 149 | unsigned room, real_n; | ||
| 150 | |||
| 151 | while (1) { | ||
| 152 | 279682 | room = av_bprint_room(buf); | |
| 153 |
2/2✓ Branch 0 taken 279579 times.
✓ Branch 1 taken 103 times.
|
279682 | if (size < room) |
| 154 | 279579 | break; | |
| 155 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 103 times.
|
103 | if (av_bprint_alloc(buf, size)) |
| 156 | ✗ | break; | |
| 157 | } | ||
| 158 |
1/2✓ Branch 0 taken 279579 times.
✗ Branch 1 not taken.
|
279579 | if (room) { |
| 159 | 279579 | real_n = FFMIN(size, room - 1); | |
| 160 | 279579 | memcpy(buf->str + buf->len, data, real_n); | |
| 161 | } | ||
| 162 | 279579 | av_bprint_grow(buf, size); | |
| 163 | 279579 | } | |
| 164 | |||
| 165 | 2 | void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm) | |
| 166 | { | ||
| 167 | unsigned room; | ||
| 168 | size_t l; | ||
| 169 | 2 | size_t fmt_len = strlen(fmt); | |
| 170 | |||
| 171 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!*fmt) |
| 172 | ✗ | return; | |
| 173 | while (1) { | ||
| 174 | 2 | room = av_bprint_room(buf); | |
| 175 |
3/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
2 | if (room && (l = strftime(buf->str + buf->len, room, fmt, tm))) |
| 176 | 1 | break; | |
| 177 | |||
| 178 | /* Due to the limitations of strftime() it is not possible to know if | ||
| 179 | * the output buffer is too small or the output is empty. | ||
| 180 | * However, a 256x output space requirement compared to the format | ||
| 181 | * string length is so unlikely we can safely assume empty output. This | ||
| 182 | * allows supporting possibly empty format strings like "%p". */ | ||
| 183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (room >> 8 > fmt_len) |
| 184 | ✗ | break; | |
| 185 | |||
| 186 | /* strftime does not tell us how much room it would need: let us | ||
| 187 | retry with twice as much until the buffer is large enough */ | ||
| 188 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | room = !room ? fmt_len + 1 : |
| 189 | room <= INT_MAX / 2 ? room * 2 : INT_MAX; | ||
| 190 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (av_bprint_alloc(buf, room)) { |
| 191 | /* impossible to grow, try to manage something useful anyway */ | ||
| 192 | 1 | room = av_bprint_room(buf); | |
| 193 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (room < 1024) { |
| 194 | /* if strftime fails because the buffer has (almost) reached | ||
| 195 | its maximum size, let us try in a local buffer; 1k should | ||
| 196 | be enough to format any real date+time string */ | ||
| 197 | char buf2[1024]; | ||
| 198 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) { |
| 199 | 1 | av_bprintf(buf, "%s", buf2); | |
| 200 | 1 | return; | |
| 201 | } | ||
| 202 | } | ||
| 203 | ✗ | if (room) { | |
| 204 | /* if anything else failed and the buffer is not already | ||
| 205 | truncated, let us add a stock string and force truncation */ | ||
| 206 | static const char txt[] = "[truncated strftime output]"; | ||
| 207 | ✗ | memset(buf->str + buf->len, '!', room); | |
| 208 | ✗ | memcpy(buf->str + buf->len, txt, FFMIN(sizeof(txt) - 1, room)); | |
| 209 | ✗ | av_bprint_grow(buf, room); /* force truncation */ | |
| 210 | } | ||
| 211 | ✗ | return; | |
| 212 | } | ||
| 213 | } | ||
| 214 | 1 | av_bprint_grow(buf, l); | |
| 215 | } | ||
| 216 | |||
| 217 | 21 | void av_bprint_get_buffer(AVBPrint *buf, unsigned size, | |
| 218 | unsigned char **mem, unsigned *actual_size) | ||
| 219 | { | ||
| 220 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
|
21 | if (size > av_bprint_room(buf)) |
| 221 | 14 | av_bprint_alloc(buf, size); | |
| 222 | 21 | *actual_size = av_bprint_room(buf); | |
| 223 |
1/2✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
|
21 | *mem = *actual_size ? (unsigned char *) (buf->str + buf->len) : NULL; |
| 224 | 21 | } | |
| 225 | |||
| 226 | 18050996 | void av_bprint_clear(AVBPrint *buf) | |
| 227 | { | ||
| 228 |
2/2✓ Branch 0 taken 16755983 times.
✓ Branch 1 taken 1295013 times.
|
18050996 | if (buf->len) { |
| 229 | 16755983 | *buf->str = 0; | |
| 230 | 16755983 | buf->len = 0; | |
| 231 | } | ||
| 232 | 18050996 | } | |
| 233 | |||
| 234 | 821928 | int av_bprint_finalize(AVBPrint *buf, char **ret_str) | |
| 235 | { | ||
| 236 | 821928 | unsigned real_size = FFMIN(buf->len + 1, buf->size); | |
| 237 | char *str; | ||
| 238 | 821928 | int ret = 0; | |
| 239 | |||
| 240 |
2/2✓ Branch 0 taken 15791 times.
✓ Branch 1 taken 806137 times.
|
821928 | if (ret_str) { |
| 241 |
2/2✓ Branch 0 taken 75 times.
✓ Branch 1 taken 15716 times.
|
15791 | if (av_bprint_is_allocated(buf)) { |
| 242 | 75 | str = av_realloc(buf->str, real_size); | |
| 243 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | if (!str) |
| 244 | ✗ | str = buf->str; | |
| 245 | 75 | buf->str = NULL; | |
| 246 | } else { | ||
| 247 | 15716 | str = av_memdup(buf->str, real_size); | |
| 248 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15716 times.
|
15716 | if (!str) |
| 249 | ✗ | ret = AVERROR(ENOMEM); | |
| 250 | } | ||
| 251 | 15791 | *ret_str = str; | |
| 252 | } else { | ||
| 253 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 806126 times.
|
806137 | if (av_bprint_is_allocated(buf)) |
| 254 | 11 | av_freep(&buf->str); | |
| 255 | } | ||
| 256 | 821928 | buf->size = real_size; | |
| 257 | 821928 | return ret; | |
| 258 | } | ||
| 259 | |||
| 260 | #define WHITESPACES " \n\t\r" | ||
| 261 | |||
| 262 | 10769 | void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, | |
| 263 | enum AVEscapeMode mode, int flags) | ||
| 264 | { | ||
| 265 | 10769 | const char *src0 = src; | |
| 266 | |||
| 267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10769 times.
|
10769 | if (mode == AV_ESCAPE_MODE_AUTO) |
| 268 | ✗ | mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */ | |
| 269 | |||
| 270 |
2/3✗ Branch 0 not taken.
✓ Branch 1 taken 1293 times.
✓ Branch 2 taken 9476 times.
|
10769 | switch (mode) { |
| 271 | ✗ | case AV_ESCAPE_MODE_QUOTE: | |
| 272 | /* enclose the string between '' */ | ||
| 273 | ✗ | av_bprint_chars(dstbuf, '\'', 1); | |
| 274 | ✗ | for (; *src; src++) { | |
| 275 | ✗ | if (*src == '\'') | |
| 276 | ✗ | av_bprintf(dstbuf, "'\\''"); | |
| 277 | else | ||
| 278 | ✗ | av_bprint_chars(dstbuf, *src, 1); | |
| 279 | } | ||
| 280 | ✗ | av_bprint_chars(dstbuf, '\'', 1); | |
| 281 | ✗ | break; | |
| 282 | |||
| 283 | 1293 | case AV_ESCAPE_MODE_XML: | |
| 284 | /* escape XML non-markup character data as per 2.4 by default: */ | ||
| 285 | /* [^<&]* - ([^<&]* ']]>' [^<&]*) */ | ||
| 286 | |||
| 287 | /* additionally, given one of the AV_ESCAPE_FLAG_XML_* flags, */ | ||
| 288 | /* escape those specific characters as required. */ | ||
| 289 |
2/2✓ Branch 0 taken 21205 times.
✓ Branch 1 taken 1293 times.
|
22498 | for (; *src; src++) { |
| 290 |
6/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 39 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 21131 times.
|
21205 | switch (*src) { |
| 291 | 2 | case '&' : av_bprintf(dstbuf, "%s", "&"); break; | |
| 292 | 17 | case '<' : av_bprintf(dstbuf, "%s", "<"); break; | |
| 293 | 12 | case '>' : av_bprintf(dstbuf, "%s", ">"); break; | |
| 294 | 39 | case '\'': | |
| 295 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES)) |
| 296 | 39 | goto XML_DEFAULT_HANDLING; | |
| 297 | |||
| 298 | ✗ | av_bprintf(dstbuf, "%s", "'"); | |
| 299 | ✗ | break; | |
| 300 | 4 | case '"' : | |
| 301 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES)) |
| 302 | ✗ | goto XML_DEFAULT_HANDLING; | |
| 303 | |||
| 304 | 4 | av_bprintf(dstbuf, "%s", """); | |
| 305 | 4 | break; | |
| 306 | 39 | XML_DEFAULT_HANDLING: | |
| 307 | 21170 | default: av_bprint_chars(dstbuf, *src, 1); | |
| 308 | } | ||
| 309 | } | ||
| 310 | 1293 | break; | |
| 311 | |||
| 312 | /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */ | ||
| 313 | 9476 | default: | |
| 314 | /* \-escape characters */ | ||
| 315 |
2/2✓ Branch 0 taken 231629 times.
✓ Branch 1 taken 9476 times.
|
241105 | for (; *src; src++) { |
| 316 |
4/4✓ Branch 0 taken 222161 times.
✓ Branch 1 taken 9468 times.
✓ Branch 2 taken 9460 times.
✓ Branch 3 taken 212701 times.
|
231629 | int is_first_last = src == src0 || !*(src+1); |
| 317 | 231629 | int is_ws = !!strchr(WHITESPACES, *src); | |
| 318 |
3/4✓ Branch 0 taken 231629 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
✓ Branch 3 taken 231559 times.
|
231629 | int is_strictly_special = special_chars && strchr(special_chars, *src); |
| 319 | 231629 | int is_special = | |
| 320 |
6/6✓ Branch 0 taken 231559 times.
✓ Branch 1 taken 70 times.
✓ Branch 2 taken 231509 times.
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 51 times.
✓ Branch 5 taken 231458 times.
|
231680 | is_strictly_special || strchr("'\\", *src) || |
| 321 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
|
51 | (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE)); |
| 322 | |||
| 323 |
2/2✓ Branch 0 taken 231559 times.
✓ Branch 1 taken 70 times.
|
231629 | if (is_strictly_special || |
| 324 |
3/4✓ Branch 0 taken 231559 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 231509 times.
✓ Branch 3 taken 50 times.
|
231559 | (!(flags & AV_ESCAPE_FLAG_STRICT) && |
| 325 |
3/4✓ Branch 0 taken 51 times.
✓ Branch 1 taken 231458 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 51 times.
|
231509 | (is_special || (is_ws && is_first_last)))) |
| 326 | 120 | av_bprint_chars(dstbuf, '\\', 1); | |
| 327 | 231629 | av_bprint_chars(dstbuf, *src, 1); | |
| 328 | } | ||
| 329 | 9476 | break; | |
| 330 | } | ||
| 331 | 10769 | } | |
| 332 |