Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
F
ffmpeg.wasm-core
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Linshizhi
ffmpeg.wasm-core
Commits
f0bb1a59
Commit
f0bb1a59
authored
Apr 07, 2012
by
Stefano Sabatini
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ffprobe: use avbprint API
Simplify, increase robustness.
parent
6101e532
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
81 additions
and
184 deletions
+81
-184
ffprobe.c
ffprobe.c
+81
-184
No files found.
ffprobe.c
View file @
f0bb1a59
...
...
@@ -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
(
"&"
);
break
;
case
'<'
:
size
+=
strlen
(
"<"
);
break
;
case
'>'
:
size
+=
strlen
(
">"
);
break
;
case
'\"'
:
size
+=
strlen
(
"""
);
break
;
case
'\''
:
size
+=
strlen
(
"'"
);
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
(
"&"
);
break
;
case
'<'
:
COPY_STR
(
"<"
);
break
;
case
'>'
:
COPY_STR
(
">"
);
break
;
case
'\"'
:
COPY_STR
(
"""
);
break
;
case
'\''
:
COPY_STR
(
"'"
);
break
;
default:
*
q
++
=
*
p
;
case
'&'
:
av_bprintf
(
dst
,
"%s"
,
"&"
);
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
)
;
}
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
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment