ffprobe: use avbprint API
authorStefano Sabatini <stefasab@gmail.com>
Sat, 7 Apr 2012 14:19:13 +0000 (16:19 +0200)
committerStefano Sabatini <stefasab@gmail.com>
Sat, 7 Apr 2012 22:02:21 +0000 (00:02 +0200)
Simplify, increase robustness.

ffprobe.c

index 80eace9..8a76a0b 100644 (file)
--- a/ffprobe.c
+++ b/ffprobe.c
@@ -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);
+            quote = 1;
 
-    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;
 
-    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, key,   wctx));
+    av_bprint_finalize(&buf, NULL);
+
+    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,