mirror of
https://github.com/mpv-player/mpv
synced 2025-01-01 04:36:24 +01:00
options: commandline: accept --foo=xyz style options
Allow writing commandline options with two leading dashes. In this mode a parameter for the option, if any, follows after a '='; following separate commandline arguments are never consumed as a parameter to a previous double-dash option. Flag options may omit parameter and behave like old single-dash syntax. "--fs=yes", "--fs=no" and "--fs" are all valid; the first two behave like configuration file "fs=yes" and "fs=no", and last is the same as old "-fs" (same effect as "--fs=yes").
This commit is contained in:
parent
8bd13d50cf
commit
5d5ca22a6d
@ -3018,7 +3018,8 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||
break;
|
||||
|
||||
case MP_CMD_LOADLIST:{
|
||||
play_tree_t *e = parse_playlist_file(mpctx->mconfig, cmd->args[0].v.s);
|
||||
play_tree_t *e = parse_playlist_file(mpctx->mconfig,
|
||||
bstr(cmd->args[0].v.s));
|
||||
if (!e)
|
||||
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
||||
"\nUnable to load playlist %s.\n", cmd->args[0].v.s);
|
||||
|
@ -1052,7 +1052,7 @@ static int libmpdemux_was_interrupted(struct MPContext *mpctx, int stop_play)
|
||||
|
||||
static int playtree_add_playlist(struct MPContext *mpctx, play_tree_t* entry)
|
||||
{
|
||||
play_tree_add_bpf(entry,mpctx->filename);
|
||||
play_tree_add_bpf(entry, bstr(mpctx->filename));
|
||||
|
||||
{
|
||||
if(!entry) {
|
||||
@ -4191,7 +4191,7 @@ while (opts->player_idle_mode && !mpctx->filename) {
|
||||
// The entry is added to the main playtree after the switch().
|
||||
break;
|
||||
case MP_CMD_LOADLIST:
|
||||
entry = parse_playlist_file(mpctx->mconfig, cmd->args[0].v.s);
|
||||
entry = parse_playlist_file(mpctx->mconfig, bstr(cmd->args[0].v.s));
|
||||
break;
|
||||
case MP_CMD_QUIT:
|
||||
exit_player_with_rc(mpctx, EXIT_QUIT, (cmd->nargs > 0)? cmd->args[0].v.i : 0);
|
||||
|
214
parser-mpcmd.c
214
parser-mpcmd.c
@ -32,33 +32,12 @@
|
||||
#include "parser-mpcmd.h"
|
||||
#include "osdep/macosx_finder_args.h"
|
||||
|
||||
static int mode = 0;
|
||||
|
||||
#define GLOBAL 0
|
||||
#define LOCAL 1
|
||||
#define DROP_LOCAL 2
|
||||
|
||||
#define dvd_range(a) (a > 0 && a < 256)
|
||||
|
||||
|
||||
static int is_entry_option(struct m_config *mconfig, char *opt, char *param,
|
||||
play_tree_t **ret)
|
||||
{
|
||||
*ret = NULL;
|
||||
|
||||
if (strcasecmp(opt, "playlist") == 0) { // We handle playlist here
|
||||
if (!param)
|
||||
return M_OPT_MISSING_PARAM;
|
||||
|
||||
*ret = parse_playlist_file(mconfig, param);
|
||||
if (!*ret)
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void add_entry(play_tree_t **last_parentp,
|
||||
play_tree_t **last_entryp, play_tree_t *entry)
|
||||
{
|
||||
@ -69,16 +48,36 @@ static inline void add_entry(play_tree_t **last_parentp,
|
||||
*last_entryp = entry;
|
||||
}
|
||||
|
||||
static bool split_opt(struct bstr *opt, struct bstr *param, bool *old_syntax)
|
||||
{
|
||||
if (!bstr_startswith0(*opt, "-") || opt->len == 1)
|
||||
return false;
|
||||
if (bstr_startswith0(*opt, "--")) {
|
||||
*old_syntax = false;
|
||||
*opt = bstr_cut(*opt, 2);
|
||||
*param = bstr(NULL);
|
||||
int idx = bstrchr(*opt, '=');
|
||||
if (idx > 0) {
|
||||
*param = bstr_cut(*opt, idx + 1);
|
||||
*opt = bstr_splice(*opt, 0, idx);
|
||||
}
|
||||
} else {
|
||||
*old_syntax = true;
|
||||
*opt = bstr_cut(*opt, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Parse command line to set up config and playtree
|
||||
play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
|
||||
char **argv)
|
||||
{
|
||||
int i, j, start_title = -1, end_title = -1;
|
||||
char *opt, *splitpos = NULL;
|
||||
char entbuf[15];
|
||||
int mode = 0;
|
||||
bool no_more_opts = false;
|
||||
int opt_exit = 0; // whether mplayer should exit without playing anything
|
||||
bool opt_exit = false; // exit immediately after parsing (help options)
|
||||
play_tree_t *last_parent, *last_entry = NULL, *root;
|
||||
struct bstr orig_opt;
|
||||
|
||||
assert(config != NULL);
|
||||
assert(argv != NULL);
|
||||
@ -94,15 +93,16 @@ play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
|
||||
|
||||
last_parent = root = play_tree_new();
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
//next:
|
||||
opt = argv[i];
|
||||
struct bstr opt = bstr(argv[i]);
|
||||
orig_opt = opt;
|
||||
/* check for -- (no more options id.) except --help! */
|
||||
if (!strcmp(opt, "--")) {
|
||||
if (!bstrcmp0(opt, "--")) {
|
||||
no_more_opts = true;
|
||||
continue;
|
||||
}
|
||||
if ((opt[0] == '{') && (opt[1] == '\0')) {
|
||||
if (!bstrcmp0(opt, "{")) {
|
||||
play_tree_t *entry = play_tree_new();
|
||||
mode = LOCAL;
|
||||
if (last_parent->flags & PLAY_TREE_RND)
|
||||
@ -117,7 +117,7 @@ play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((opt[0] == '}') && (opt[1] == '\0')) {
|
||||
if (!bstrcmp0(opt, "}")) {
|
||||
if (!last_parent || !last_parent->parent) {
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "too much }-\n");
|
||||
goto err_out;
|
||||
@ -127,101 +127,87 @@ play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((no_more_opts == 0) && (*opt == '-') && (*(opt + 1) != 0)) {
|
||||
int tmp = 0;
|
||||
/* remove trailing '-' */
|
||||
opt++;
|
||||
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_DBG3, "this_opt = option: %s\n", opt);
|
||||
// We handle here some specific option
|
||||
// Loop option when it apply to a group
|
||||
if (strcasecmp(opt, "loop") == 0 &&
|
||||
struct bstr param = bstr(i+1 < argc ? argv[i+1] : NULL);
|
||||
bool old_syntax;
|
||||
if (!no_more_opts && split_opt(&opt, ¶m, &old_syntax)) {
|
||||
// Handle some special arguments outside option parser.
|
||||
// --loop when it applies to a group of files (per-file is option)
|
||||
if (bstrcasecmp0(opt, "loop") == 0 &&
|
||||
(!last_entry || last_entry->child)) {
|
||||
int l;
|
||||
char *end = NULL;
|
||||
l = (i + 1 < argc) ? strtol(argv[i + 1], &end, 0) : 0;
|
||||
if (!end || *end != '\0') {
|
||||
struct bstr rest;
|
||||
int l = bstrtoll(param, &rest, 0);
|
||||
if (!param.len || rest.len) {
|
||||
mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
|
||||
"The loop option must be an integer: %s\n",
|
||||
argv[i + 1]);
|
||||
tmp = ERR_OUT_OF_RANGE;
|
||||
"The loop option must be an integer: \"%.*s\"\n",
|
||||
BSTR_P(param));
|
||||
goto print_err;
|
||||
} else {
|
||||
play_tree_t *pt = last_entry ? last_entry : last_parent;
|
||||
l = l <= 0 ? -1 : l;
|
||||
pt->loop = l;
|
||||
tmp = 1;
|
||||
i += old_syntax;
|
||||
}
|
||||
} else if (strcasecmp(opt, "shuffle") == 0) {
|
||||
} else if (bstrcasecmp0(opt, "shuffle") == 0) {
|
||||
if (last_entry && last_entry->child)
|
||||
last_entry->flags |= PLAY_TREE_RND;
|
||||
else
|
||||
last_parent->flags |= PLAY_TREE_RND;
|
||||
} else if (strcasecmp(opt, "noshuffle") == 0) {
|
||||
} else if (bstrcasecmp0(opt, "noshuffle") == 0 ||
|
||||
bstrcasecmp0(opt, "no-shuffle") == 0) {
|
||||
if (last_entry && last_entry->child)
|
||||
last_entry->flags &= ~PLAY_TREE_RND;
|
||||
else
|
||||
last_parent->flags &= ~PLAY_TREE_RND;
|
||||
} else if (bstrcasecmp0(opt, "playlist") == 0) {
|
||||
if (param.len <= 0)
|
||||
goto print_err;
|
||||
struct play_tree *entry = parse_playlist_file(config, param);
|
||||
if (!entry)
|
||||
goto print_err;
|
||||
add_entry(&last_parent, &last_entry, entry);
|
||||
if ((last_parent->flags & PLAY_TREE_RND) && entry->child)
|
||||
entry->flags |= PLAY_TREE_RND;
|
||||
mode = LOCAL;
|
||||
i += old_syntax;
|
||||
} else {
|
||||
const m_option_t *mp_opt = NULL;
|
||||
play_tree_t *entry = NULL;
|
||||
|
||||
tmp = is_entry_option(config, opt,
|
||||
(i + 1 < argc) ? argv[i + 1] : NULL, &entry);
|
||||
if (tmp > 0) { // It's an entry
|
||||
if (entry) {
|
||||
add_entry(&last_parent, &last_entry, entry);
|
||||
if ((last_parent->flags & PLAY_TREE_RND)
|
||||
&& entry->child)
|
||||
entry->flags |= PLAY_TREE_RND;
|
||||
mode = LOCAL;
|
||||
} else if (mode == LOCAL) // Drop params for empty entry
|
||||
mode = DROP_LOCAL;
|
||||
} else if (tmp == 0) { // 'normal' options
|
||||
mp_opt = m_config_get_option(config, bstr(opt));
|
||||
if (mp_opt != NULL) { // Option exist
|
||||
if (mode == GLOBAL || (mp_opt->flags & M_OPT_GLOBAL))
|
||||
tmp = (i + 1 < argc)
|
||||
? m_config_set_option0(config, opt, argv[i + 1],
|
||||
true)
|
||||
: m_config_set_option0(config, opt, NULL, false);
|
||||
else {
|
||||
tmp = m_config_check_option0(config, opt,
|
||||
(i + 1 < argc) ? argv[i + 1] : NULL, true);
|
||||
if (tmp >= 0 && mode != DROP_LOCAL) {
|
||||
play_tree_t *pt =
|
||||
last_entry ? last_entry : last_parent;
|
||||
play_tree_set_param(pt, opt, argv[i + 1]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tmp = M_OPT_UNKNOWN;
|
||||
mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
|
||||
"Unknown option on the command line: -%s\n",
|
||||
opt);
|
||||
// "normal" options
|
||||
const struct m_option *mp_opt;
|
||||
mp_opt = m_config_get_option(config, opt);
|
||||
if (!mp_opt) {
|
||||
mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
|
||||
"Unknown option on the command line: --%.*s\n",
|
||||
BSTR_P(opt));
|
||||
goto print_err;
|
||||
}
|
||||
int r;
|
||||
if (mode == GLOBAL || (mp_opt->flags & M_OPT_GLOBAL)) {
|
||||
r = m_config_set_option(config, opt, param, old_syntax);
|
||||
} else {
|
||||
r = m_config_check_option(config, opt, param, old_syntax);
|
||||
if (r >= 0) {
|
||||
play_tree_t *pt = last_entry ? last_entry : last_parent;
|
||||
play_tree_set_param(pt, opt, param);
|
||||
}
|
||||
}
|
||||
if (r <= M_OPT_EXIT) {
|
||||
opt_exit = true;
|
||||
r = M_OPT_EXIT - r;
|
||||
} else if (r < 0)
|
||||
goto print_err;
|
||||
if (old_syntax)
|
||||
i += r;
|
||||
}
|
||||
|
||||
if (tmp <= M_OPT_EXIT) {
|
||||
opt_exit = 1;
|
||||
tmp = M_OPT_EXIT - tmp;
|
||||
} else if (tmp < 0) {
|
||||
mp_tmsg(MSGT_CFGPARSER, MSGL_FATAL,
|
||||
"Error parsing option on the command line: -%s\n",
|
||||
opt);
|
||||
goto err_out;
|
||||
}
|
||||
i += tmp;
|
||||
} else { /* filename */
|
||||
} else { /* filename */
|
||||
int is_dvdnav = strstr(argv[i], "dvdnav://") != NULL;
|
||||
play_tree_t *entry = play_tree_new();
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_DBG2, "Adding file %s\n", argv[i]);
|
||||
// expand DVD filename entries like dvd://1-3 into component titles
|
||||
if (strstr(argv[i], "dvd://") != NULL || is_dvdnav) {
|
||||
int offset = is_dvdnav ? 9 : 6;
|
||||
splitpos = strstr(argv[i] + offset, "-");
|
||||
char *splitpos = strstr(argv[i] + offset, "-");
|
||||
if (splitpos != NULL) {
|
||||
start_title = strtol(argv[i] + offset, NULL, 10);
|
||||
int start_title = strtol(argv[i] + offset, NULL, 10);
|
||||
int end_title;
|
||||
//entries like dvd://-2 imply start at title 1
|
||||
if (start_title < 0) {
|
||||
end_title = abs(start_title);
|
||||
@ -231,9 +217,10 @@ play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
|
||||
|
||||
if (dvd_range(start_title) && dvd_range(end_title)
|
||||
&& (start_title < end_title)) {
|
||||
for (j = start_title; j <= end_title; j++) {
|
||||
for (int j = start_title; j <= end_title; j++) {
|
||||
if (j != start_title)
|
||||
entry = play_tree_new();
|
||||
char entbuf[15];
|
||||
snprintf(entbuf, sizeof(entbuf),
|
||||
is_dvdnav ? "dvdnav://%d" : "dvd://%d", j);
|
||||
play_tree_add_file(entry, entbuf);
|
||||
@ -263,6 +250,10 @@ play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Missing }- ?\n");
|
||||
return root;
|
||||
|
||||
print_err:
|
||||
mp_tmsg(MSGT_CFGPARSER, MSGL_FATAL,
|
||||
"Error parsing option on the command line: %.*s\n",
|
||||
BSTR_P(orig_opt));
|
||||
err_out:
|
||||
play_tree_free(root, 1);
|
||||
return NULL;
|
||||
@ -286,25 +277,22 @@ int m_config_preparse_command_line(m_config_t *config, int argc, char **argv)
|
||||
config->mode = M_COMMAND_LINE_PRE_PARSE;
|
||||
|
||||
for (int i = 1 ; i < argc ; i++) {
|
||||
const struct m_option *opt;
|
||||
char *arg = argv[i];
|
||||
// Ignore non option
|
||||
if (arg[0] != '-' || arg[1] == 0) continue;
|
||||
struct bstr opt = bstr(argv[i]);
|
||||
// No more options after --
|
||||
if (!strcmp(arg, "--"))
|
||||
if (!bstrcmp0(opt, "--"))
|
||||
break;
|
||||
arg++;
|
||||
|
||||
opt = m_config_get_option(config, bstr(arg));
|
||||
// Ignore invalid option
|
||||
if (!opt)
|
||||
struct bstr param = bstr(i+1 < argc ? argv[i+1] : NULL);
|
||||
bool old_syntax;
|
||||
if (!split_opt(&opt, ¶m, &old_syntax))
|
||||
continue; // Ignore non-option arguments
|
||||
// Ignore invalid options
|
||||
if (!m_config_get_option(config, opt))
|
||||
continue;
|
||||
// Set, non-pre-parse options will be ignored
|
||||
int r = m_config_set_option0(config, arg, i+1 < argc ? argv[i+1] : NULL,
|
||||
true);
|
||||
int r = m_config_set_option(config, opt, param, old_syntax);
|
||||
if (r < 0)
|
||||
ret = r;
|
||||
else
|
||||
else if (old_syntax)
|
||||
i += r;
|
||||
}
|
||||
|
||||
|
@ -346,7 +346,7 @@ play_tree_remove_file(play_tree_t* pt,const char* file) {
|
||||
}
|
||||
|
||||
void
|
||||
play_tree_set_param(play_tree_t* pt, const char* name, const char* val) {
|
||||
play_tree_set_param(play_tree_t* pt, struct bstr name, struct bstr val) {
|
||||
int n = 0;
|
||||
|
||||
#ifdef MP_DEBUG
|
||||
@ -358,8 +358,8 @@ play_tree_set_param(play_tree_t* pt, const char* name, const char* val) {
|
||||
for ( ; pt->params[n].name != NULL ; n++ ) { }
|
||||
|
||||
pt->params = talloc_realloc(NULL, pt->params, struct play_tree_param, n + 2);
|
||||
pt->params[n].name = talloc_strdup(pt->params, name);
|
||||
pt->params[n].value = val != NULL ? talloc_strdup(pt->params, val) : NULL;
|
||||
pt->params[n].name = bstrdup0(pt->params, name);
|
||||
pt->params[n].value = bstrdup0(pt->params, val);
|
||||
memset(&pt->params[n+1],0,sizeof(play_tree_param_t));
|
||||
|
||||
return;
|
||||
@ -410,7 +410,7 @@ play_tree_set_params_from(play_tree_t* dest,play_tree_t* src) {
|
||||
return;
|
||||
|
||||
for(i = 0; src->params[i].name != NULL ; i++)
|
||||
play_tree_set_param(dest,src->params[i].name,src->params[i].value);
|
||||
play_tree_set_param(dest, bstr(src->params[i].name), bstr(src->params[i].value));
|
||||
if(src->flags & PLAY_TREE_RND) // pass the random flag too
|
||||
dest->flags |= PLAY_TREE_RND;
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
||||
#ifndef MPLAYER_PLAYTREE_H
|
||||
#define MPLAYER_PLAYTREE_H
|
||||
|
||||
#include "bstr.h"
|
||||
|
||||
/// \file
|
||||
/// \ingroup Playtree
|
||||
|
||||
@ -188,7 +190,7 @@ play_tree_remove_file(play_tree_t* pt,const char* file);
|
||||
|
||||
/// Add a config paramter to an item.
|
||||
void
|
||||
play_tree_set_param(play_tree_t* pt, const char* name, const char* val);
|
||||
play_tree_set_param(play_tree_t* pt, struct bstr name, struct bstr val);
|
||||
|
||||
/// Remove a config parameter from an item.
|
||||
int
|
||||
@ -257,7 +259,7 @@ play_tree_cleanup(play_tree_t* pt);
|
||||
/** \ingroup PlaytreeParser
|
||||
*/
|
||||
play_tree_t*
|
||||
parse_playlist_file(struct m_config *mconfig, char* file);
|
||||
parse_playlist_file(struct m_config *mconfig, struct bstr file);
|
||||
|
||||
/// \defgroup PtAPI Playtree highlevel API
|
||||
/// \ingroup Playtree
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "asxparser.h"
|
||||
#include "m_config.h"
|
||||
#include "playtree.h"
|
||||
@ -355,7 +357,7 @@ parse_pls(play_tree_parser_t* p) {
|
||||
entry = play_tree_new();
|
||||
play_tree_add_file(entry,entries[num].file);
|
||||
if (entries[num].length)
|
||||
play_tree_set_param(entry, "endpos", entries[num].length);
|
||||
play_tree_set_param(entry, bstr("endpos"), bstr(entries[num].length));
|
||||
free(entries[num].file);
|
||||
if(list)
|
||||
play_tree_append_entry(last_entry,entry);
|
||||
@ -853,13 +855,13 @@ play_tree_add_basepath(play_tree_t* pt, char* bp) {
|
||||
}
|
||||
|
||||
// Wrapper for play_tree_add_basepath (add base path from file)
|
||||
void play_tree_add_bpf(play_tree_t* pt, char* filename)
|
||||
void play_tree_add_bpf(play_tree_t *pt, struct bstr filename)
|
||||
{
|
||||
char *ls, *file;
|
||||
|
||||
if (pt && filename)
|
||||
if (pt)
|
||||
{
|
||||
file = strdup(filename);
|
||||
file = bstrdup0(NULL, filename);
|
||||
if (file)
|
||||
{
|
||||
ls = strrchr(file,'/');
|
||||
@ -868,25 +870,30 @@ void play_tree_add_bpf(play_tree_t* pt, char* filename)
|
||||
ls[1] = '\0';
|
||||
play_tree_add_basepath(pt,file);
|
||||
}
|
||||
free(file);
|
||||
talloc_free(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
play_tree_t*
|
||||
parse_playlist_file(struct m_config *mconfig, char* file) {
|
||||
parse_playlist_file(struct m_config *mconfig, struct bstr file) {
|
||||
stream_t *stream;
|
||||
play_tree_t* ret;
|
||||
int f=DEMUXER_TYPE_PLAYLIST;
|
||||
|
||||
stream = open_stream(file,0,&f);
|
||||
char *file0 = bstrdup0(NULL, file);
|
||||
stream = open_stream(file0, 0, &f);
|
||||
talloc_free(file0);
|
||||
|
||||
if(!stream) {
|
||||
mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Error while opening playlist file %s: %s\n",file,strerror(errno));
|
||||
mp_msg(MSGT_PLAYTREE,MSGL_ERR,
|
||||
"Error while opening playlist file %.*s: %s\n",
|
||||
BSTR_P(file), strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mp_msg(MSGT_PLAYTREE,MSGL_V,"Parsing playlist file %s...\n",file);
|
||||
mp_msg(MSGT_PLAYTREE, MSGL_V,
|
||||
"Parsing playlist file %.*s...\n", BSTR_P(file));
|
||||
|
||||
ret = parse_playtree(stream, mconfig, 1);
|
||||
free_stream(stream);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#define MPLAYER_PLAYTREEPARSER_H
|
||||
|
||||
#include "playtree.h"
|
||||
#include "bstr.h"
|
||||
|
||||
/// \defgroup PlaytreeParser Playtree parser
|
||||
/// \ingroup Playtree
|
||||
@ -65,7 +66,7 @@ play_tree_parser_get_play_tree(play_tree_parser_t* p, int forced);
|
||||
|
||||
/// Wrapper for play_tree_add_basepath (add base path from file).
|
||||
void
|
||||
play_tree_add_bpf(play_tree_t* pt, char* filename);
|
||||
play_tree_add_bpf(play_tree_t* pt, struct bstr filename);
|
||||
|
||||
///@}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user