/* * Copyright (c) The FFmpeg developers * * 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 #include #include #include #include #include "avtextformat.h" #include "libavutil/bprint.h" #include "libavutil/error.h" #include "libavutil/opt.h" #include "tf_internal.h" /* Flat output */ typedef struct FlatContext { const AVClass *class; const char *sep_str; char sep; int hierarchical; } FlatContext; #undef OFFSET #define OFFSET(x) offsetof(FlatContext, x) static const AVOption flat_options[] = { { "sep_char", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, { .str = "." }, 0, 0 }, { "s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, { .str = "." }, 0, 0 }, { "hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 }, { "h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 }, { NULL }, }; DEFINE_FORMATTER_CLASS(flat); static av_cold int flat_init(AVTextFormatContext *wctx) { FlatContext *flat = wctx->priv; if (strlen(flat->sep_str) != 1) { av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n", flat->sep_str); return AVERROR(EINVAL); } flat->sep = flat->sep_str[0]; return 0; } static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep) { const char *p; for (p = src; *p; p++) { if (!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))) av_bprint_chars(dst, '_', 1); else av_bprint_chars(dst, *p, 1); } return dst->str; } static const char *flat_escape_value_str(AVBPrint *dst, const char *src) { const char *p; for (p = src; *p; p++) { switch (*p) { case '\n': av_bprintf(dst, "%s", "\\n"); break; case '\r': av_bprintf(dst, "%s", "\\r"); break; case '\\': av_bprintf(dst, "%s", "\\\\"); break; case '"': av_bprintf(dst, "%s", "\\\""); break; case '`': av_bprintf(dst, "%s", "\\`"); break; case '$': av_bprintf(dst, "%s", "\\$"); break; default: av_bprint_chars(dst, *p, 1); break; } } return dst->str; } static void flat_print_section_header(AVTextFormatContext *wctx, const void *data) { FlatContext *flat = wctx->priv; AVBPrint *buf = &wctx->section_pbuf[wctx->level]; const AVTextFormatSection *section = tf_get_section(wctx, wctx->level); const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level); if (!section) return; /* build section header */ av_bprint_clear(buf); if (!parent_section) return; av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level - 1].str); if (flat->hierarchical || !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY | AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER))) { av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str); if (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) { int n = parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE ? wctx->nb_item_type[wctx->level - 1][section->id] : wctx->nb_item[wctx->level - 1]; av_bprintf(buf, "%d%s", n, flat->sep_str); } } } static void flat_print_int(AVTextFormatContext *wctx, const char *key, int64_t value) { writer_printf(wctx, "%s%s=%"PRId64"\n", wctx->section_pbuf[wctx->level].str, key, value); } static void flat_print_str(AVTextFormatContext *wctx, const char *key, const char *value) { FlatContext *flat = wctx->priv; AVBPrint buf; writer_put_str(wctx, wctx->section_pbuf[wctx->level].str); av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); writer_printf(wctx, "%s=", flat_escape_key_str(&buf, key, flat->sep)); av_bprint_clear(&buf); writer_printf(wctx, "\"%s\"\n", flat_escape_value_str(&buf, value)); av_bprint_finalize(&buf, NULL); } const AVTextFormatter avtextformatter_flat = { .name = "flat", .priv_size = sizeof(FlatContext), .init = flat_init, .print_section_header = flat_print_section_header, .print_integer = flat_print_int, .print_string = flat_print_str, .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS | AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT, .priv_class = &flat_class, };