diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /media/ffvpx/libavutil/bprint.c | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'media/ffvpx/libavutil/bprint.c')
-rw-r--r-- | media/ffvpx/libavutil/bprint.c | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/media/ffvpx/libavutil/bprint.c b/media/ffvpx/libavutil/bprint.c new file mode 100644 index 0000000000..5b540ebc9e --- /dev/null +++ b/media/ffvpx/libavutil/bprint.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include "avstring.h" +#include "bprint.h" +#include "compat/va_copy.h" +#include "error.h" +#include "macros.h" +#include "mem.h" + +#define av_bprint_room(buf) ((buf)->size - FFMIN((buf)->len, (buf)->size)) +#define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer) + +static int av_bprint_alloc(AVBPrint *buf, unsigned room) +{ + char *old_str, *new_str; + unsigned min_size, new_size; + + if (buf->size == buf->size_max) + return AVERROR(EIO); + if (!av_bprint_is_complete(buf)) + return AVERROR_INVALIDDATA; /* it is already truncated anyway */ + min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room); + new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2; + if (new_size < min_size) + new_size = FFMIN(buf->size_max, min_size); + old_str = av_bprint_is_allocated(buf) ? buf->str : NULL; + new_str = av_realloc(old_str, new_size); + if (!new_str) + return AVERROR(ENOMEM); + if (!old_str) + memcpy(new_str, buf->str, buf->len + 1); + buf->str = new_str; + buf->size = new_size; + return 0; +} + +static void av_bprint_grow(AVBPrint *buf, unsigned extra_len) +{ + /* arbitrary margin to avoid small overflows */ + extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len); + buf->len += extra_len; + if (buf->size) + buf->str[FFMIN(buf->len, buf->size - 1)] = 0; +} + +void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max) +{ + unsigned size_auto = (char *)buf + sizeof(*buf) - + buf->reserved_internal_buffer; + + if (size_max == 1) + size_max = size_auto; + buf->str = buf->reserved_internal_buffer; + buf->len = 0; + buf->size = FFMIN(size_auto, size_max); + buf->size_max = size_max; + *buf->str = 0; + if (size_init > buf->size) + av_bprint_alloc(buf, size_init - 1); +} + +void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size) +{ + buf->str = buffer; + buf->len = 0; + buf->size = size; + buf->size_max = size; + *buf->str = 0; +} + +void av_bprintf(AVBPrint *buf, const char *fmt, ...) +{ + unsigned room; + char *dst; + va_list vl; + int extra_len; + + while (1) { + room = av_bprint_room(buf); + dst = room ? buf->str + buf->len : NULL; + va_start(vl, fmt); + extra_len = vsnprintf(dst, room, fmt, vl); + va_end(vl); + if (extra_len <= 0) + return; + if (extra_len < room) + break; + if (av_bprint_alloc(buf, extra_len)) + break; + } + av_bprint_grow(buf, extra_len); +} + +void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg) +{ + unsigned room; + char *dst; + int extra_len; + va_list vl; + + while (1) { + room = av_bprint_room(buf); + dst = room ? buf->str + buf->len : NULL; + va_copy(vl, vl_arg); + extra_len = vsnprintf(dst, room, fmt, vl); + va_end(vl); + if (extra_len <= 0) + return; + if (extra_len < room) + break; + if (av_bprint_alloc(buf, extra_len)) + break; + } + av_bprint_grow(buf, extra_len); +} + +void av_bprint_chars(AVBPrint *buf, char c, unsigned n) +{ + unsigned room, real_n; + + while (1) { + room = av_bprint_room(buf); + if (n < room) + break; + if (av_bprint_alloc(buf, n)) + break; + } + if (room) { + real_n = FFMIN(n, room - 1); + memset(buf->str + buf->len, c, real_n); + } + av_bprint_grow(buf, n); +} + +void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size) +{ + unsigned room, real_n; + + while (1) { + room = av_bprint_room(buf); + if (size < room) + break; + if (av_bprint_alloc(buf, size)) + break; + } + if (room) { + real_n = FFMIN(size, room - 1); + memcpy(buf->str + buf->len, data, real_n); + } + av_bprint_grow(buf, size); +} + +void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm) +{ + unsigned room; + size_t l; + + if (!*fmt) + return; + while (1) { + room = av_bprint_room(buf); + if (room && (l = strftime(buf->str + buf->len, room, fmt, tm))) + break; + /* strftime does not tell us how much room it would need: let us + retry with twice as much until the buffer is large enough */ + room = !room ? strlen(fmt) + 1 : + room <= INT_MAX / 2 ? room * 2 : INT_MAX; + if (av_bprint_alloc(buf, room)) { + /* impossible to grow, try to manage something useful anyway */ + room = av_bprint_room(buf); + if (room < 1024) { + /* if strftime fails because the buffer has (almost) reached + its maximum size, let us try in a local buffer; 1k should + be enough to format any real date+time string */ + char buf2[1024]; + if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) { + av_bprintf(buf, "%s", buf2); + return; + } + } + if (room) { + /* if anything else failed and the buffer is not already + truncated, let us add a stock string and force truncation */ + static const char txt[] = "[truncated strftime output]"; + memset(buf->str + buf->len, '!', room); + memcpy(buf->str + buf->len, txt, FFMIN(sizeof(txt) - 1, room)); + av_bprint_grow(buf, room); /* force truncation */ + } + return; + } + } + av_bprint_grow(buf, l); +} + +void av_bprint_get_buffer(AVBPrint *buf, unsigned size, + unsigned char **mem, unsigned *actual_size) +{ + if (size > av_bprint_room(buf)) + av_bprint_alloc(buf, size); + *actual_size = av_bprint_room(buf); + *mem = *actual_size ? buf->str + buf->len : NULL; +} + +void av_bprint_clear(AVBPrint *buf) +{ + if (buf->len) { + *buf->str = 0; + buf->len = 0; + } +} + +int av_bprint_finalize(AVBPrint *buf, char **ret_str) +{ + unsigned real_size = FFMIN(buf->len + 1, buf->size); + char *str; + int ret = 0; + + if (ret_str) { + if (av_bprint_is_allocated(buf)) { + str = av_realloc(buf->str, real_size); + if (!str) + str = buf->str; + buf->str = NULL; + } else { + str = av_memdup(buf->str, real_size); + if (!str) + ret = AVERROR(ENOMEM); + } + *ret_str = str; + } else { + if (av_bprint_is_allocated(buf)) + av_freep(&buf->str); + } + buf->size = real_size; + return ret; +} + +#define WHITESPACES " \n\t\r" + +void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, + enum AVEscapeMode mode, int flags) +{ + const char *src0 = src; + + if (mode == AV_ESCAPE_MODE_AUTO) + mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */ + + switch (mode) { + case AV_ESCAPE_MODE_QUOTE: + /* enclose the string between '' */ + av_bprint_chars(dstbuf, '\'', 1); + for (; *src; src++) { + if (*src == '\'') + av_bprintf(dstbuf, "'\\''"); + else + av_bprint_chars(dstbuf, *src, 1); + } + av_bprint_chars(dstbuf, '\'', 1); + break; + + case AV_ESCAPE_MODE_XML: + /* escape XML non-markup character data as per 2.4 by default: */ + /* [^<&]* - ([^<&]* ']]>' [^<&]*) */ + + /* additionally, given one of the AV_ESCAPE_FLAG_XML_* flags, */ + /* escape those specific characters as required. */ + for (; *src; src++) { + switch (*src) { + case '&' : av_bprintf(dstbuf, "%s", "&"); break; + case '<' : av_bprintf(dstbuf, "%s", "<"); break; + case '>' : av_bprintf(dstbuf, "%s", ">"); break; + case '\'': + if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES)) + goto XML_DEFAULT_HANDLING; + + av_bprintf(dstbuf, "%s", "'"); + break; + case '"' : + if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES)) + goto XML_DEFAULT_HANDLING; + + av_bprintf(dstbuf, "%s", """); + break; +XML_DEFAULT_HANDLING: + default: av_bprint_chars(dstbuf, *src, 1); + } + } + break; + + /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */ + default: + /* \-escape characters */ + for (; *src; src++) { + int is_first_last = src == src0 || !*(src+1); + int is_ws = !!strchr(WHITESPACES, *src); + int is_strictly_special = special_chars && strchr(special_chars, *src); + int is_special = + is_strictly_special || strchr("'\\", *src) || + (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE)); + + if (is_strictly_special || + (!(flags & AV_ESCAPE_FLAG_STRICT) && + (is_special || (is_ws && is_first_last)))) + av_bprint_chars(dstbuf, '\\', 1); + av_bprint_chars(dstbuf, *src, 1); + } + break; + } +} |