| 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 "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 | 67477 | static int av_bprint_alloc(AVBPrint *buf, unsigned room) | |
| 37 | { | ||
| 38 | char *old_str, *new_str; | ||
| 39 | unsigned min_size, new_size; | ||
| 40 | |||
| 41 |
2/2✓ Branch 0 taken 67304 times.
✓ Branch 1 taken 173 times.
|
67477 | if (buf->size == buf->size_max) |
| 42 | 67304 | return AVERROR(EIO); | |
| 43 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 173 times.
|
173 | if (!av_bprint_is_complete(buf)) |
| 44 | ✗ | return AVERROR_INVALIDDATA; /* it is already truncated anyway */ | |
| 45 | 173 | min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room); | |
| 46 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 172 times.
|
173 | new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2; |
| 47 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 149 times.
|
173 | if (new_size < min_size) |
| 48 | 24 | new_size = FFMIN(buf->size_max, min_size); | |
| 49 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 76 times.
|
173 | old_str = av_bprint_is_allocated(buf) ? buf->str : NULL; |
| 50 | 173 | new_str = av_realloc(old_str, new_size); | |
| 51 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 173 times.
|
173 | if (!new_str) |
| 52 | ✗ | return AVERROR(ENOMEM); | |
| 53 |
2/2✓ Branch 0 taken 76 times.
✓ Branch 1 taken 97 times.
|
173 | if (!old_str) |
| 54 | 76 | memcpy(new_str, buf->str, buf->len + 1); | |
| 55 | 173 | buf->str = new_str; | |
| 56 | 173 | buf->size = new_size; | |
| 57 | 173 | return 0; | |
| 58 | } | ||
| 59 | |||
| 60 | 14182000 | static void av_bprint_grow(AVBPrint *buf, unsigned extra_len) | |
| 61 | { | ||
| 62 | /* arbitrary margin to avoid small overflows */ | ||
| 63 | 14182000 | extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len); | |
| 64 | 14182000 | buf->len += extra_len; | |
| 65 |
2/2✓ Branch 0 taken 14115378 times.
✓ Branch 1 taken 66622 times.
|
14182000 | if (buf->size) |
| 66 | 14115378 | buf->str[FFMIN(buf->len, buf->size - 1)] = 0; | |
| 67 | 14182000 | } | |
| 68 | |||
| 69 | 2545073 | void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max) | |
| 70 | { | ||
| 71 | 2545073 | unsigned size_auto = (char *)buf + sizeof(*buf) - | |
| 72 | buf->reserved_internal_buffer; | ||
| 73 | |||
| 74 |
2/2✓ Branch 0 taken 1874293 times.
✓ Branch 1 taken 670780 times.
|
2545073 | if (size_max == AV_BPRINT_SIZE_AUTOMATIC) |
| 75 | 1874293 | size_max = size_auto; | |
| 76 | 2545073 | buf->str = buf->reserved_internal_buffer; | |
| 77 | 2545073 | buf->len = 0; | |
| 78 | 2545073 | buf->size = FFMIN(size_auto, size_max); | |
| 79 | 2545073 | buf->size_max = size_max; | |
| 80 | 2545073 | *buf->str = 0; | |
| 81 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 2545032 times.
|
2545073 | if (size_init > buf->size) |
| 82 | 41 | av_bprint_alloc(buf, size_init - 1); | |
| 83 | 2545073 | } | |
| 84 | |||
| 85 | 29730 | void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size) | |
| 86 | { | ||
| 87 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 29698 times.
|
29730 | if (size == 0) { |
| 88 | 32 | av_bprint_init(buf, 0, AV_BPRINT_SIZE_COUNT_ONLY); | |
| 89 | 32 | return; | |
| 90 | } | ||
| 91 | |||
| 92 | 29698 | buf->str = buffer; | |
| 93 | 29698 | buf->len = 0; | |
| 94 | 29698 | buf->size = size; | |
| 95 | 29698 | buf->size_max = size; | |
| 96 | 29698 | *buf->str = 0; | |
| 97 | } | ||
| 98 | |||
| 99 | 1745691 | void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg) | |
| 100 | { | ||
| 101 | unsigned room; | ||
| 102 | char *dst; | ||
| 103 | int extra_len; | ||
| 104 | va_list vl; | ||
| 105 | |||
| 106 | while (1) { | ||
| 107 | 1745708 | room = av_bprint_room(buf); | |
| 108 |
2/2✓ Branch 0 taken 1744269 times.
✓ Branch 1 taken 1439 times.
|
1745708 | dst = room ? buf->str + buf->len : NULL; |
| 109 | 1745708 | va_copy(vl, vl_arg); | |
| 110 | 1745708 | extra_len = vsnprintf(dst, room, fmt, vl); | |
| 111 | 1745708 | va_end(vl); | |
| 112 |
2/2✓ Branch 0 taken 17122 times.
✓ Branch 1 taken 1728586 times.
|
1745708 | if (extra_len <= 0) |
| 113 | 17122 | return; | |
| 114 |
2/2✓ Branch 0 taken 1727126 times.
✓ Branch 1 taken 1460 times.
|
1728586 | if (extra_len < room) |
| 115 | 1727126 | break; | |
| 116 |
2/2✓ Branch 1 taken 1443 times.
✓ Branch 2 taken 17 times.
|
1460 | if (av_bprint_alloc(buf, extra_len)) |
| 117 | 1443 | break; | |
| 118 | } | ||
| 119 | 1728569 | av_bprint_grow(buf, extra_len); | |
| 120 | } | ||
| 121 | |||
| 122 | 1270114 | void av_bprintf(AVBPrint *buf, const char *fmt, ...) | |
| 123 | { | ||
| 124 | va_list vl; | ||
| 125 | 1270114 | va_start(vl, fmt); | |
| 126 | 1270114 | av_vbprintf(buf, fmt, vl); | |
| 127 | 1270114 | va_end(vl); | |
| 128 | 1270114 | } | |
| 129 | |||
| 130 | 12209660 | void av_bprint_chars(AVBPrint *buf, char c, unsigned n) | |
| 131 | { | ||
| 132 | unsigned room, real_n; | ||
| 133 | |||
| 134 | while (1) { | ||
| 135 | 12209660 | room = av_bprint_room(buf); | |
| 136 |
2/2✓ Branch 0 taken 12143800 times.
✓ Branch 1 taken 65860 times.
|
12209660 | if (n < room) |
| 137 | 12143800 | break; | |
| 138 |
1/2✓ Branch 1 taken 65860 times.
✗ Branch 2 not taken.
|
65860 | if (av_bprint_alloc(buf, n)) |
| 139 | 65860 | break; | |
| 140 | } | ||
| 141 |
2/2✓ Branch 0 taken 12143800 times.
✓ Branch 1 taken 65860 times.
|
12209660 | if (room) { |
| 142 | 12143800 | real_n = FFMIN(n, room - 1); | |
| 143 | 12143800 | memset(buf->str + buf->len, c, real_n); | |
| 144 | } | ||
| 145 | 12209660 | av_bprint_grow(buf, n); | |
| 146 | 12209660 | } | |
| 147 | |||
| 148 | 243770 | void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size) | |
| 149 | { | ||
| 150 | unsigned room, real_n; | ||
| 151 | |||
| 152 | while (1) { | ||
| 153 | 243873 | room = av_bprint_room(buf); | |
| 154 |
2/2✓ Branch 0 taken 243770 times.
✓ Branch 1 taken 103 times.
|
243873 | if (size < room) |
| 155 | 243770 | break; | |
| 156 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 103 times.
|
103 | if (av_bprint_alloc(buf, size)) |
| 157 | ✗ | break; | |
| 158 | } | ||
| 159 |
1/2✓ Branch 0 taken 243770 times.
✗ Branch 1 not taken.
|
243770 | if (room) { |
| 160 | 243770 | real_n = FFMIN(size, room - 1); | |
| 161 | 243770 | memcpy(buf->str + buf->len, data, real_n); | |
| 162 | } | ||
| 163 | 243770 | av_bprint_grow(buf, size); | |
| 164 | 243770 | } | |
| 165 | |||
| 166 | 2 | void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm) | |
| 167 | { | ||
| 168 | unsigned room; | ||
| 169 | size_t l; | ||
| 170 | 2 | size_t fmt_len = strlen(fmt); | |
| 171 | |||
| 172 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!*fmt) |
| 173 | ✗ | return; | |
| 174 | while (1) { | ||
| 175 | 2 | room = av_bprint_room(buf); | |
| 176 |
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))) |
| 177 | 1 | break; | |
| 178 | |||
| 179 | /* Due to the limitations of strftime() it is not possible to know if | ||
| 180 | * the output buffer is too small or the output is empty. | ||
| 181 | * However, a 256x output space requirement compared to the format | ||
| 182 | * string length is so unlikely we can safely assume empty output. This | ||
| 183 | * allows supporting possibly empty format strings like "%p". */ | ||
| 184 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (room >> 8 > fmt_len) |
| 185 | ✗ | break; | |
| 186 | |||
| 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 |
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 : |
| 190 | room <= INT_MAX / 2 ? room * 2 : INT_MAX; | ||
| 191 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (av_bprint_alloc(buf, room)) { |
| 192 | /* impossible to grow, try to manage something useful anyway */ | ||
| 193 | 1 | room = av_bprint_room(buf); | |
| 194 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | 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 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) { |
| 200 | 1 | av_bprintf(buf, "%s", buf2); | |
| 201 | 1 | 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 | 1 | av_bprint_grow(buf, l); | |
| 216 | } | ||
| 217 | |||
| 218 | 18 | void av_bprint_get_buffer(AVBPrint *buf, unsigned size, | |
| 219 | unsigned char **mem, unsigned *actual_size) | ||
| 220 | { | ||
| 221 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | if (size > av_bprint_room(buf)) |
| 222 | 12 | av_bprint_alloc(buf, size); | |
| 223 | 18 | *actual_size = av_bprint_room(buf); | |
| 224 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | *mem = *actual_size ? buf->str + buf->len : NULL; |
| 225 | 18 | } | |
| 226 | |||
| 227 | 85686 | void av_bprint_clear(AVBPrint *buf) | |
| 228 | { | ||
| 229 |
2/2✓ Branch 0 taken 61917 times.
✓ Branch 1 taken 23769 times.
|
85686 | if (buf->len) { |
| 230 | 61917 | *buf->str = 0; | |
| 231 | 61917 | buf->len = 0; | |
| 232 | } | ||
| 233 | 85686 | } | |
| 234 | |||
| 235 | 778613 | int av_bprint_finalize(AVBPrint *buf, char **ret_str) | |
| 236 | { | ||
| 237 | 778613 | unsigned real_size = FFMIN(buf->len + 1, buf->size); | |
| 238 | char *str; | ||
| 239 | 778613 | int ret = 0; | |
| 240 | |||
| 241 |
2/2✓ Branch 0 taken 13150 times.
✓ Branch 1 taken 765463 times.
|
778613 | if (ret_str) { |
| 242 |
2/2✓ Branch 0 taken 69 times.
✓ Branch 1 taken 13081 times.
|
13150 | if (av_bprint_is_allocated(buf)) { |
| 243 | 69 | str = av_realloc(buf->str, real_size); | |
| 244 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
|
69 | if (!str) |
| 245 | ✗ | str = buf->str; | |
| 246 | 69 | buf->str = NULL; | |
| 247 | } else { | ||
| 248 | 13081 | str = av_memdup(buf->str, real_size); | |
| 249 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13081 times.
|
13081 | if (!str) |
| 250 | ✗ | ret = AVERROR(ENOMEM); | |
| 251 | } | ||
| 252 | 13150 | *ret_str = str; | |
| 253 | } else { | ||
| 254 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 765453 times.
|
765463 | if (av_bprint_is_allocated(buf)) |
| 255 | 10 | av_freep(&buf->str); | |
| 256 | } | ||
| 257 | 778613 | buf->size = real_size; | |
| 258 | 778613 | return ret; | |
| 259 | } | ||
| 260 | |||
| 261 | #define WHITESPACES " \n\t\r" | ||
| 262 | |||
| 263 | 10519 | void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, | |
| 264 | enum AVEscapeMode mode, int flags) | ||
| 265 | { | ||
| 266 | 10519 | const char *src0 = src; | |
| 267 | |||
| 268 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10519 times.
|
10519 | if (mode == AV_ESCAPE_MODE_AUTO) |
| 269 | ✗ | mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */ | |
| 270 | |||
| 271 |
2/3✗ Branch 0 not taken.
✓ Branch 1 taken 1293 times.
✓ Branch 2 taken 9226 times.
|
10519 | 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 | 1293 | 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 |
2/2✓ Branch 0 taken 21205 times.
✓ Branch 1 taken 1293 times.
|
22498 | for (; *src; src++) { |
| 291 |
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) { |
| 292 | 2 | case '&' : av_bprintf(dstbuf, "%s", "&"); break; | |
| 293 | 17 | case '<' : av_bprintf(dstbuf, "%s", "<"); break; | |
| 294 | 12 | case '>' : av_bprintf(dstbuf, "%s", ">"); break; | |
| 295 | 39 | case '\'': | |
| 296 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES)) |
| 297 | 39 | goto XML_DEFAULT_HANDLING; | |
| 298 | |||
| 299 | ✗ | av_bprintf(dstbuf, "%s", "'"); | |
| 300 | ✗ | break; | |
| 301 | 4 | case '"' : | |
| 302 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES)) |
| 303 | ✗ | goto XML_DEFAULT_HANDLING; | |
| 304 | |||
| 305 | 4 | av_bprintf(dstbuf, "%s", """); | |
| 306 | 4 | break; | |
| 307 | 39 | XML_DEFAULT_HANDLING: | |
| 308 | 21170 | default: av_bprint_chars(dstbuf, *src, 1); | |
| 309 | } | ||
| 310 | } | ||
| 311 | 1293 | break; | |
| 312 | |||
| 313 | /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */ | ||
| 314 | 9226 | default: | |
| 315 | /* \-escape characters */ | ||
| 316 |
2/2✓ Branch 0 taken 226578 times.
✓ Branch 1 taken 9226 times.
|
235804 | for (; *src; src++) { |
| 317 |
4/4✓ Branch 0 taken 217361 times.
✓ Branch 1 taken 9217 times.
✓ Branch 2 taken 9211 times.
✓ Branch 3 taken 208150 times.
|
226578 | int is_first_last = src == src0 || !*(src+1); |
| 318 | 226578 | int is_ws = !!strchr(WHITESPACES, *src); | |
| 319 |
3/4✓ Branch 0 taken 226578 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
✓ Branch 3 taken 226510 times.
|
226578 | int is_strictly_special = special_chars && strchr(special_chars, *src); |
| 320 | 226578 | int is_special = | |
| 321 |
6/6✓ Branch 0 taken 226510 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 226460 times.
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 26 times.
✓ Branch 5 taken 226434 times.
|
226604 | is_strictly_special || strchr("'\\", *src) || |
| 322 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE)); |
| 323 | |||
| 324 |
2/2✓ Branch 0 taken 226510 times.
✓ Branch 1 taken 68 times.
|
226578 | if (is_strictly_special || |
| 325 |
3/4✓ Branch 0 taken 226510 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 226460 times.
✓ Branch 3 taken 50 times.
|
226510 | (!(flags & AV_ESCAPE_FLAG_STRICT) && |
| 326 |
3/4✓ Branch 0 taken 26 times.
✓ Branch 1 taken 226434 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
|
226460 | (is_special || (is_ws && is_first_last)))) |
| 327 | 118 | av_bprint_chars(dstbuf, '\\', 1); | |
| 328 | 226578 | av_bprint_chars(dstbuf, *src, 1); | |
| 329 | } | ||
| 330 | 9226 | break; | |
| 331 | } | ||
| 332 | 10519 | } | |
| 333 |