Commit f0bb1a59 authored by Stefano Sabatini's avatar Stefano Sabatini

ffprobe: use avbprint API

Simplify, increase robustness.
parent 6101e532
......@@ -29,6 +29,7 @@
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/dict.h"
......@@ -384,31 +385,6 @@ fail:
return NULL;
}
#define ESCAPE_INIT_BUF_SIZE 256
#define ESCAPE_CHECK_SIZE(src, size, max_size) \
if (size > max_size) { \
char buf[64]; \
snprintf(buf, sizeof(buf), "%s", src); \
av_log(log_ctx, AV_LOG_WARNING, \
"String '%s...' is too big\n", buf); \
return "FFPROBE_TOO_BIG_STRING"; \
}
#define ESCAPE_REALLOC_BUF(dst_size_p, dst_p, src, size) \
if (*dst_size_p < size) { \
char *q = av_realloc(*dst_p, size); \
if (!q) { \
char buf[64]; \
snprintf(buf, sizeof(buf), "%s", src); \
av_log(log_ctx, AV_LOG_WARNING, \
"String '%s...' could not be escaped\n", buf); \
return "FFPROBE_THIS_STRING_COULD_NOT_BE_ESCAPED"; \
} \
*dst_size_p = size; \
*dst = q; \
}
/* WRITERS */
/* Default output */
......@@ -487,81 +463,51 @@ static const Writer default_writer = {
* Escape \n, \r, \\ and sep characters contained in s, and print the
* resulting string.
*/
static const char *c_escape_str(char **dst, size_t *dst_size,
const char *src, const char sep, void *log_ctx)
static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
{
const char *p;
char *q;
size_t size = 1;
/* precompute size */
for (p = src; *p; p++, size++) {
ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-2);
if (*p == '\n' || *p == '\r' || *p == '\\')
size++;
}
ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
q = *dst;
for (p = src; *p; p++) {
switch (*src) {
case '\n': *q++ = '\\'; *q++ = 'n'; break;
case '\r': *q++ = '\\'; *q++ = 'r'; break;
case '\\': *q++ = '\\'; *q++ = '\\'; break;
case '\n': av_bprintf(dst, "%s", "\\n"); break;
case '\r': av_bprintf(dst, "%s", "\\r"); break;
case '\\': av_bprintf(dst, "%s", "\\\\"); break;
default:
if (*p == sep)
*q++ = '\\';
*q++ = *p;
av_bprint_chars(dst, '\\', 1);
av_bprint_chars(dst, *p, 1);
}
}
*q = 0;
return *dst;
return dst->str;
}
/**
* Quote fields containing special characters, check RFC4180.
*/
static const char *csv_escape_str(char **dst, size_t *dst_size,
const char *src, const char sep, void *log_ctx)
static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
{
const char *p;
char *q;
size_t size = 1;
int quote = 0;
/* precompute size */
for (p = src; *p; p++, size++) {
ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-4);
/* check if input needs quoting */
for (p = src; *p; p++)
if (*p == '"' || *p == sep || *p == '\n' || *p == '\r')
if (!quote) {
quote = 1;
size += 2;
}
if (*p == '"')
size++;
}
ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
q = *dst;
p = src;
if (quote)
*q++ = '\"';
while (*p) {
av_bprint_chars(dst, '\"', 1);
for (p = src; *p; p++) {
if (*p == '"')
*q++ = '\"';
*q++ = *p++;
av_bprint_chars(dst, '\"', 1);
av_bprint_chars(dst, *p, 1);
}
if (quote)
*q++ = '\"';
*q = 0;
return *dst;
av_bprint_chars(dst, '\"', 1);
return dst->str;
}
static const char *none_escape_str(char **dst, size_t *dst_size,
const char *src, const char sep, void *log_ctx)
static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
{
return src;
}
......@@ -571,11 +517,8 @@ typedef struct CompactContext {
char *item_sep_str;
char item_sep;
int nokey;
char *buf;
size_t buf_size;
char *escape_mode_str;
const char * (*escape_str)(char **dst, size_t *dst_size,
const char *src, const char sep, void *log_ctx);
const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
} CompactContext;
#define OFFSET(x) offsetof(CompactContext, x)
......@@ -621,10 +564,6 @@ static av_cold int compact_init(WriterContext *wctx, const char *args, void *opa
}
compact->item_sep = compact->item_sep_str[0];
compact->buf_size = ESCAPE_INIT_BUF_SIZE;
if (!(compact->buf = av_malloc(compact->buf_size)))
return AVERROR(ENOMEM);
if (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str;
else if (!strcmp(compact->escape_mode_str, "c" )) compact->escape_str = c_escape_str;
else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str;
......@@ -641,7 +580,6 @@ static av_cold void compact_uninit(WriterContext *wctx)
CompactContext *compact = wctx->priv;
av_freep(&compact->item_sep_str);
av_freep(&compact->buf);
av_freep(&compact->escape_mode_str);
}
......@@ -660,12 +598,14 @@ static void compact_print_section_footer(WriterContext *wctx, const char *sectio
static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
{
CompactContext *compact = wctx->priv;
AVBPrint buf;
if (wctx->nb_item) printf("%c", compact->item_sep);
if (!compact->nokey)
printf("%s=", key);
printf("%s", compact->escape_str(&compact->buf, &compact->buf_size,
value, compact->item_sep, wctx));
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("%s", compact->escape_str(&buf, value, compact->item_sep, wctx));
av_bprint_finalize(&buf, NULL);
}
static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
......@@ -682,14 +622,20 @@ static void compact_show_tags(WriterContext *wctx, AVDictionary *dict)
{
CompactContext *compact = wctx->priv;
AVDictionaryEntry *tag = NULL;
AVBPrint buf;
while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
if (wctx->nb_item) printf("%c", compact->item_sep);
if (!compact->nokey)
printf("tag:%s=", compact->escape_str(&compact->buf, &compact->buf_size,
tag->key, compact->item_sep, wctx));
printf("%s", compact->escape_str(&compact->buf, &compact->buf_size,
tag->value, compact->item_sep, wctx));
if (!compact->nokey) {
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("tag:%s=", compact->escape_str(&buf, tag->key, compact->item_sep, wctx));
av_bprint_finalize(&buf, NULL);
}
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("%s", compact->escape_str(&buf, tag->value, compact->item_sep, wctx));
av_bprint_finalize(&buf, NULL);
}
}
......@@ -731,8 +677,6 @@ static const Writer csv_writer = {
typedef struct {
const AVClass *class;
int multiple_entries; ///< tells if the given chapter requires multiple entries
char *buf;
size_t buf_size;
int print_packets_and_frames;
int indent_level;
int compact;
......@@ -776,52 +720,27 @@ static av_cold int json_init(WriterContext *wctx, const char *args, void *opaque
json->item_sep = json->compact ? ", " : ",\n";
json->item_start_end = json->compact ? " " : "\n";
json->buf_size = ESCAPE_INIT_BUF_SIZE;
if (!(json->buf = av_malloc(json->buf_size)))
return AVERROR(ENOMEM);
return 0;
}
static av_cold void json_uninit(WriterContext *wctx)
{
JSONContext *json = wctx->priv;
av_freep(&json->buf);
}
static const char *json_escape_str(char **dst, size_t *dst_size, const char *src,
void *log_ctx)
static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
{
static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0};
const char *p;
char *q;
size_t size = 1;
// compute the length of the escaped string
for (p = src; *p; p++) {
ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-6);
if (strchr(json_escape, *p)) size += 2; // simple escape
else if ((unsigned char)*p < 32) size += 6; // handle non-printable chars
else size += 1; // char copy
}
ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
q = *dst;
for (p = src; *p; p++) {
char *s = strchr(json_escape, *p);
if (s) {
*q++ = '\\';
*q++ = json_subst[s - json_escape];
av_bprint_chars(dst, '\\', 1);
av_bprint_chars(dst, json_subst[s - json_escape], 1);
} else if ((unsigned char)*p < 32) {
snprintf(q, 7, "\\u00%02x", *p & 0xff);
q += 6;
av_bprintf(dst, "\\u00%02x", *p & 0xff);
} else {
*q++ = *p;
av_bprint_chars(dst, *p, 1);
}
}
*q = 0;
return *dst;
return dst->str;
}
static void json_print_header(WriterContext *wctx)
......@@ -843,6 +762,7 @@ static void json_print_footer(WriterContext *wctx)
static void json_print_chapter_header(WriterContext *wctx, const char *chapter)
{
JSONContext *json = wctx->priv;
AVBPrint buf;
if (wctx->nb_chapter)
printf(",");
......@@ -852,7 +772,9 @@ static void json_print_chapter_header(WriterContext *wctx, const char *chapter)
!strcmp(chapter, "streams") || !strcmp(chapter, "library_versions");
if (json->multiple_entries) {
JSON_INDENT();
printf("\"%s\": [\n", json_escape_str(&json->buf, &json->buf_size, chapter, wctx));
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("\"%s\": [\n", json_escape_str(&buf, chapter, wctx));
av_bprint_finalize(&buf, NULL);
json->print_packets_and_frames = !strcmp(chapter, "packets_and_frames");
json->indent_level++;
}
......@@ -903,10 +825,15 @@ static void json_print_section_footer(WriterContext *wctx, const char *section)
static inline void json_print_item_str(WriterContext *wctx,
const char *key, const char *value)
{
JSONContext *json = wctx->priv;
AVBPrint buf;
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("\"%s\":", json_escape_str(&buf, key, wctx));
av_bprint_finalize(&buf, NULL);
printf("\"%s\":", json_escape_str(&json->buf, &json->buf_size, key, wctx));
printf(" \"%s\"", json_escape_str(&json->buf, &json->buf_size, value, wctx));
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf(" \"%s\"", json_escape_str(&buf, value, wctx));
av_bprint_finalize(&buf, NULL);
}
static void json_print_str(WriterContext *wctx, const char *key, const char *value)
......@@ -922,12 +849,15 @@ static void json_print_str(WriterContext *wctx, const char *key, const char *val
static void json_print_int(WriterContext *wctx, const char *key, long long int value)
{
JSONContext *json = wctx->priv;
AVBPrint buf;
if (wctx->nb_item) printf("%s", json->item_sep);
if (!json->compact)
JSON_INDENT();
printf("\"%s\": %lld",
json_escape_str(&json->buf, &json->buf_size, key, wctx), value);
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("\"%s\": %lld", json_escape_str(&buf, key, wctx), value);
av_bprint_finalize(&buf, NULL);
}
static void json_show_tags(WriterContext *wctx, AVDictionary *dict)
......@@ -960,7 +890,6 @@ static const Writer json_writer = {
.name = "json",
.priv_size = sizeof(JSONContext),
.init = json_init,
.uninit = json_uninit,
.print_header = json_print_header,
.print_footer = json_print_footer,
.print_chapter_header = json_print_chapter_header,
......@@ -982,8 +911,6 @@ typedef struct {
int indent_level;
int fully_qualified;
int xsd_strict;
char *buf;
size_t buf_size;
} XMLContext;
#undef OFFSET
......@@ -1043,61 +970,25 @@ static av_cold int xml_init(WriterContext *wctx, const char *args, void *opaque)
}
}
xml->buf_size = ESCAPE_INIT_BUF_SIZE;
if (!(xml->buf = av_malloc(xml->buf_size)))
return AVERROR(ENOMEM);
return 0;
}
static av_cold void xml_uninit(WriterContext *wctx)
{
XMLContext *xml = wctx->priv;
av_freep(&xml->buf);
}
static const char *xml_escape_str(char **dst, size_t *dst_size, const char *src,
void *log_ctx)
static const char *xml_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
{
const char *p;
char *q;
size_t size = 1;
/* precompute size */
for (p = src; *p; p++, size++) {
ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-10);
switch (*p) {
case '&' : size += strlen("&amp;"); break;
case '<' : size += strlen("&lt;"); break;
case '>' : size += strlen("&gt;"); break;
case '\"': size += strlen("&quot;"); break;
case '\'': size += strlen("&apos;"); break;
default: size++;
}
}
ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
#define COPY_STR(str) { \
const char *s = str; \
while (*s) \
*q++ = *s++; \
}
p = src;
q = *dst;
while (*p) {
for (p = src; *p; p++) {
switch (*p) {
case '&' : COPY_STR("&amp;"); break;
case '<' : COPY_STR("&lt;"); break;
case '>' : COPY_STR("&gt;"); break;
case '\"': COPY_STR("&quot;"); break;
case '\'': COPY_STR("&apos;"); break;
default: *q++ = *p;
case '&' : av_bprintf(dst, "%s", "&amp;"); break;
case '<' : av_bprintf(dst, "%s", "&lt;"); break;
case '>' : av_bprintf(dst, "%s", "&gt;"); break;
case '\"': av_bprintf(dst, "%s", "&quot;"); break;
case '\'': av_bprintf(dst, "%s", "&apos;"); break;
default: av_bprint_chars(dst, *p, 1);
}
p++;
}
*q = 0;
return *dst;
return dst->str;
}
static void xml_print_header(WriterContext *wctx)
......@@ -1172,11 +1063,13 @@ static void xml_print_section_footer(WriterContext *wctx, const char *section)
static void xml_print_str(WriterContext *wctx, const char *key, const char *value)
{
XMLContext *xml = wctx->priv;
AVBPrint buf;
if (wctx->nb_item)
printf(" ");
printf("%s=\"%s\"", key, xml_escape_str(&xml->buf, &xml->buf_size, value, wctx));
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("%s=\"%s\"", key, xml_escape_str(&buf, value, wctx));
av_bprint_finalize(&buf, NULL);
}
static void xml_print_int(WriterContext *wctx, const char *key, long long int value)
......@@ -1191,6 +1084,7 @@ static void xml_show_tags(WriterContext *wctx, AVDictionary *dict)
XMLContext *xml = wctx->priv;
AVDictionaryEntry *tag = NULL;
int is_first = 1;
AVBPrint buf;
xml->indent_level++;
while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
......@@ -1201,10 +1095,14 @@ static void xml_show_tags(WriterContext *wctx, AVDictionary *dict)
is_first = 0;
}
XML_INDENT();
printf("<tag key=\"%s\"",
xml_escape_str(&xml->buf, &xml->buf_size, tag->key, wctx));
printf(" value=\"%s\"/>\n",
xml_escape_str(&xml->buf, &xml->buf_size, tag->value, wctx));
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("<tag key=\"%s\"", xml_escape_str(&buf, tag->key, wctx));
av_bprint_finalize(&buf, NULL);
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf(" value=\"%s\"/>\n", xml_escape_str(&buf, tag->value, wctx));
av_bprint_finalize(&buf, NULL);
}
xml->indent_level--;
}
......@@ -1213,7 +1111,6 @@ static Writer xml_writer = {
.name = "xml",
.priv_size = sizeof(XMLContext),
.init = xml_init,
.uninit = xml_uninit,
.print_header = xml_print_header,
.print_footer = xml_print_footer,
.print_chapter_header = xml_print_chapter_header,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment