FFmpeg
Loading...
Searching...
No Matches
tf_compact.c
Go to the documentation of this file.
1/*
2 * Copyright (c) The FFmpeg developers
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 <inttypes.h>
22#include <limits.h>
23#include <stdarg.h>
24#include <string.h>
25
26#include "avtextformat.h"
27#include "libavutil/avutil.h"
28#include "libavutil/bprint.h"
29#include "libavutil/error.h"
30#include "libavutil/opt.h"
31#include "tf_internal.h"
32
33
34/* Compact output */
35
36/**
37 * Apply C-language-like string escaping.
38 */
39static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
40{
41 const char *p;
42
43 for (p = src; *p; p++) {
44 switch (*p) {
45 case '\b': av_bprintf(dst, "%s", "\\b"); break;
46 case '\f': av_bprintf(dst, "%s", "\\f"); break;
47 case '\n': av_bprintf(dst, "%s", "\\n"); break;
48 case '\r': av_bprintf(dst, "%s", "\\r"); break;
49 case '\\': av_bprintf(dst, "%s", "\\\\"); break;
50 default:
51 if (*p == sep)
52 av_bprint_chars(dst, '\\', 1);
53 av_bprint_chars(dst, *p, 1);
54 }
55 }
56 return dst->str;
57}
58
59/**
60 * Quote fields containing special characters, check RFC4180.
61 */
62static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
63{
64 char meta_chars[] = { sep, '"', '\n', '\r', '\0' };
65
66 int needs_quoting = !!src[strcspn(src, meta_chars)];
67
68 if (needs_quoting)
69 av_bprint_chars(dst, '"', 1);
70
71 for (; *src; src++) {
72 if (*src == '"')
73 av_bprint_chars(dst, '"', 1);
75 }
76 if (needs_quoting)
77 av_bprint_chars(dst, '"', 1);
78 return dst->str;
79}
80
81static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
82{
83 return src;
84}
85
86typedef struct CompactContext {
87 const AVClass *class;
90 int nokey;
93 const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
98
99#undef OFFSET
100#define OFFSET(x) offsetof(CompactContext, x)
101
102static const AVOption compact_options[] = {
103 { "item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, { .str = "|" }, 0, 0 },
104 { "s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, { .str = "|" }, 0, 0 },
105 { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1 },
106 { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1 },
107 { "escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, { .str = "c" }, 0, 0 },
108 { "e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, { .str = "c" }, 0, 0 },
109 { "print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
110 { "p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
111 { NULL },
112};
113
115
117{
118 CompactContext *compact = wctx->priv;
119
120 if (strlen(compact->item_sep_str) != 1) {
121 av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
122 compact->item_sep_str);
123 return AVERROR(EINVAL);
124 }
125 compact->item_sep = compact->item_sep_str[0];
126
127 if (!strcmp(compact->escape_mode_str, "none")) {
128 compact->escape_str = none_escape_str;
129 } else if (!strcmp(compact->escape_mode_str, "c" )) {
130 compact->escape_str = c_escape_str;
131 } else if (!strcmp(compact->escape_mode_str, "csv" )) {
132 compact->escape_str = csv_escape_str;
133 } else {
134 av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str);
135 return AVERROR(EINVAL);
136 }
137
138 return 0;
139}
140
142{
143 CompactContext *compact = wctx->priv;
144 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
145 const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
146
147 if (!section)
148 return;
149
150 compact->terminate_line[wctx->level] = 1;
151 compact->has_nested_elems[wctx->level] = 0;
152
153 av_bprint_clear(&wctx->section_pbuf[wctx->level]);
154 if (parent_section &&
158
159 /* define a prefix for elements not contained in an array or
160 in a wrapper, or for array elements with a type */
161 const char *element_name = (char *)av_x_if_null(section->element_name, section->name);
162 AVBPrint *section_pbuf = &wctx->section_pbuf[wctx->level];
163
164 compact->nested_section[wctx->level] = 1;
165 compact->has_nested_elems[wctx->level - 1] = 1;
166
167 av_bprintf(section_pbuf, "%s%s",
168 wctx->section_pbuf[wctx->level - 1].str, element_name);
169
171 // add /TYPE to prefix
172 av_bprint_chars(section_pbuf, '/', 1);
173
174 // normalize section type, replace special characters and lower case
175 for (const char *p = section->get_type(data); *p; p++) {
176 char c =
177 (*p >= '0' && *p <= '9') ||
178 (*p >= 'a' && *p <= 'z') ||
179 (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_';
180 av_bprint_chars(section_pbuf, c, 1);
181 }
182 }
183 av_bprint_chars(section_pbuf, ':', 1);
184
185 wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level - 1];
186 } else {
187 if (parent_section && !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER | AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)) &&
188 wctx->level && wctx->nb_item[wctx->level - 1])
189 writer_w8(wctx, compact->item_sep);
190 if (compact->print_section &&
192 writer_printf(wctx, "%s%c", section->name, compact->item_sep);
193 }
194}
195
197{
198 CompactContext *compact = wctx->priv;
199 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
200
201 if (!section)
202 return;
203
204 if (!compact->nested_section[wctx->level] &&
205 compact->terminate_line[wctx->level] &&
207 writer_w8(wctx, '\n');
208}
209
210static void compact_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
211{
212 CompactContext *compact = wctx->priv;
213 AVBPrint buf;
214
215 if (wctx->nb_item[wctx->level])
216 writer_w8(wctx, compact->item_sep);
217
218 if (!compact->nokey)
219 writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
220
222 writer_put_str(wctx, compact->escape_str(&buf, value, compact->item_sep, wctx));
224}
225
226static void compact_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
227{
228 CompactContext *compact = wctx->priv;
229
230 if (wctx->nb_item[wctx->level])
231 writer_w8(wctx, compact->item_sep);
232
233 if (!compact->nokey)
234 writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
235
236 writer_printf(wctx, "%"PRId64, value);
237}
238
240 .name = "compact",
241 .priv_size = sizeof(CompactContext),
243 .print_section_header = compact_print_section_header,
244 .print_section_footer = compact_print_section_footer,
245 .print_integer = compact_print_int,
246 .print_string = compact_print_str,
248 .priv_class = &compact_class,
249};
250
251/* CSV output */
252
253#undef OFFSET
254#define OFFSET(x) offsetof(CompactContext, x)
255
256static const AVOption csv_options[] = {
257 { "item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, { .str = "," }, 0, 0 },
258 { "s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, { .str = "," }, 0, 0 },
259 { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
260 { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
261 { "escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, { .str = "csv" }, 0, 0 },
262 { "e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, { .str = "csv" }, 0, 0 },
263 { "print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
264 { "p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
265 { NULL },
266};
267
269
271 .name = "csv",
272 .priv_size = sizeof(CompactContext),
274 .print_section_header = compact_print_section_header,
275 .print_section_footer = compact_print_section_footer,
276 .print_integer = compact_print_int,
277 .print_string = compact_print_str,
279 .priv_class = &csv_class,
280};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
const AVTextFormatter avtextformatter_compact
Definition tf_compact.c:239
#define AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE
For these sections the element_name field is mandatory.
#define SECTION_MAX_NB_LEVELS
#define AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS
const AVTextFormatter avtextformatter_csv
Definition tf_compact.c:270
#define AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER
the section only contains other sections, but has no data at its own level
#define AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY
the section contains an array of elements of the same type
Convenience header that includes libavutil's core.
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition bprint.c:122
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition bprint.c:69
AVBPrint public header.
#define AV_BPRINT_SIZE_UNLIMITED
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
error code definitions
double value
Definition eval.c:102
const char * key
static void print_section(SectionID id, int level)
Definition ffprobe.c:3205
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition bprint.c:235
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
Definition bprint.c:130
void av_bprint_clear(AVBPrint *buf)
Reset the string to "" but keep internal allocated data.
Definition bprint.c:227
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static void * av_x_if_null(const void *p, const void *x)
Return x default pointer in case p is NULL.
Definition avutil.h:311
static av_const int av_tolower(int c)
Locale-independent conversion of ASCII characters to lowercase.
Definition avstring.h:237
#define av_cold
Definition attributes.h:117
const char data[16]
Definition mxf.c:149
AVOptions.
Describe the class of an AVClass context structure.
Definition log.h:76
AVOption.
Definition opt.h:428
void * priv
private data for use by the filter
int level
current level, starting from 0
unsigned int nb_item[SECTION_MAX_NB_LEVELS]
number of the item printed in the given section, starting from 0
AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]
generic print buffer dedicated to each section, used by various formatters
const char * element_name
name of the contained element, if provided
const char * name
const char *(* get_type)(const void *data)
function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined
int terminate_line[SECTION_MAX_NB_LEVELS]
Definition tf_compact.c:96
int has_nested_elems[SECTION_MAX_NB_LEVELS]
Definition tf_compact.c:95
int nested_section[SECTION_MAX_NB_LEVELS]
Definition tf_compact.c:94
const char *(* escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
Definition tf_compact.c:93
char * item_sep_str
Definition tf_compact.c:88
char * escape_mode_str
Definition tf_compact.c:92
#define av_log(a,...)
#define src
Definition vp8dsp.c:248
static void compact_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
Definition tf_compact.c:210
static void compact_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
Definition tf_compact.c:226
static const AVOption csv_options[]
Definition tf_compact.c:256
static const AVOption compact_options[]
Definition tf_compact.c:102
static void compact_print_section_header(AVTextFormatContext *wctx, const void *data)
Definition tf_compact.c:141
static const char * csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
Quote fields containing special characters, check RFC4180.
Definition tf_compact.c:62
static const char * c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
Apply C-language-like string escaping.
Definition tf_compact.c:39
static void compact_print_section_footer(AVTextFormatContext *wctx)
Definition tf_compact.c:196
static av_cold int compact_init(AVTextFormatContext *wctx)
Definition tf_compact.c:116
#define OFFSET(x)
Definition tf_compact.c:100
static const char * none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
Definition tf_compact.c:81
Internal utilities for text formatters.
static const AVTextFormatSection * tf_get_section(AVTextFormatContext *tfc, int level)
Safely validate and access a section at a given level.
Definition tf_internal.h:42
static const AVTextFormatSection * tf_get_parent_section(AVTextFormatContext *tfc, int level)
Safely access the parent section.
Definition tf_internal.h:55
#define DEFINE_FORMATTER_CLASS(name)
Definition tf_internal.h:31
static void writer_w8(AVTextFormatContext *wctx, int b)
Definition tf_internal.h:63
static void writer_printf(AVTextFormatContext *wctx, const char *fmt,...)
Definition tf_internal.h:73
static void writer_put_str(AVTextFormatContext *wctx, const char *str)
Definition tf_internal.h:68
static double c[64]