mpv/core/m_option.c

2380 lines
66 KiB
C
Raw Normal View History

/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/// \file
/// \ingroup Options
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <inttypes.h>
#include <unistd.h>
#include <assert.h>
#include <libavutil/common.h>
#include <libavutil/avstring.h>
#include "talloc.h"
#include "core/mp_common.h"
#include "core/m_option.h"
#include "core/mp_msg.h"
#include "stream/url.h"
char *m_option_strerror(int code)
{
switch (code) {
case M_OPT_UNKNOWN:
return mp_gtext("option not found");
case M_OPT_MISSING_PARAM:
return mp_gtext("option requires parameter");
case M_OPT_INVALID:
return mp_gtext("option parameter could not be parsed");
case M_OPT_OUT_OF_RANGE:
return mp_gtext("parameter is outside values allowed for option");
case M_OPT_DISALLOW_PARAM:
return mp_gtext("option doesn't take a parameter");
case M_OPT_PARSER_ERR:
default:
return mp_gtext("parser error");
}
}
static const struct m_option *m_option_list_findb(const struct m_option *list,
struct bstr name)
{
for (int i = 0; list[i].name; i++) {
struct bstr lname = bstr0(list[i].name);
if ((list[i].type->flags & M_OPT_TYPE_ALLOW_WILDCARD)
&& bstr_endswith0(lname, "*")) {
lname.len--;
if (bstrcasecmp(bstr_splice(name, 0, lname.len), lname) == 0)
return &list[i];
} else if (bstrcasecmp(lname, name) == 0)
return &list[i];
}
return NULL;
}
const m_option_t *m_option_list_find(const m_option_t *list, const char *name)
{
return m_option_list_findb(list, bstr0(name));
}
// Default function that just does a memcpy
static void copy_opt(const m_option_t *opt, void *dst, const void *src)
{
if (dst && src)
memcpy(dst, src, opt->type->size);
}
// Flag
#define VAL(x) (*(int *)(x))
static int clamp_flag(const m_option_t *opt, void *val)
{
if (VAL(val) == opt->min || VAL(val) == opt->max)
return 0;
VAL(val) = opt->min;
return M_OPT_OUT_OF_RANGE;
}
static int parse_flag(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
if (param.len) {
if (!bstrcasecmp0(param, "yes")) {
if (dst)
VAL(dst) = opt->max;
return 1;
}
if (!bstrcasecmp0(param, "no")) {
if (dst)
VAL(dst) = opt->min;
return 1;
}
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Invalid parameter for %.*s flag: %.*s\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
} else {
if (dst)
VAL(dst) = opt->max;
return 0;
}
}
static char *print_flag(const m_option_t *opt, const void *val)
{
if (VAL(val) == opt->min)
return talloc_strdup(NULL, "no");
else
return talloc_strdup(NULL, "yes");
}
static void add_flag(const m_option_t *opt, void *val, double add, bool wrap)
{
if (fabs(add) < 0.5)
return;
bool state = VAL(val) != opt->min;
state = wrap ? !state : add > 0;
VAL(val) = state ? opt->max : opt->min;
}
const m_option_type_t m_option_type_flag = {
// need yes or no in config files
.name = "Flag",
.size = sizeof(int),
.flags = M_OPT_TYPE_OPTIONAL_PARAM,
.parse = parse_flag,
.print = print_flag,
.copy = copy_opt,
.add = add_flag,
.clamp = clamp_flag,
};
// Single-value, write-only flag
static int parse_store(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst)
{
if (param.len == 0 || bstrcasecmp0(param, "yes") == 0) {
if (dst)
VAL(dst) = opt->max;
return 0;
} else {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Invalid parameter for %.*s flag: %.*s\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_DISALLOW_PARAM;
}
}
const m_option_type_t m_option_type_store = {
// can only be activated
.name = "Flag",
.size = sizeof(int),
.flags = M_OPT_TYPE_OPTIONAL_PARAM,
.parse = parse_store,
};
// Integer
#undef VAL
static int clamp_longlong(const m_option_t *opt, void *val)
{
long long v = *(long long *)val;
int r = 0;
if ((opt->flags & M_OPT_MAX) && (v > opt->max)) {
v = opt->max;
r = M_OPT_OUT_OF_RANGE;
}
if ((opt->flags & M_OPT_MIN) && (v < opt->min)) {
v = opt->min;
r = M_OPT_OUT_OF_RANGE;
}
*(long long *)val = v;
return r;
}
static int parse_longlong(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
if (param.len == 0)
return M_OPT_MISSING_PARAM;
struct bstr rest;
long long tmp_int = bstrtoll(param, &rest, 10);
if (rest.len)
tmp_int = bstrtoll(param, &rest, 0);
if (rest.len) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"The %.*s option must be an integer: %.*s\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
}
if ((opt->flags & M_OPT_MIN) && (tmp_int < opt->min)) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"The %.*s option must be >= %d: %.*s\n",
BSTR_P(name), (int) opt->min, BSTR_P(param));
return M_OPT_OUT_OF_RANGE;
}
if ((opt->flags & M_OPT_MAX) && (tmp_int > opt->max)) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"The %.*s option must be <= %d: %.*s\n",
BSTR_P(name), (int) opt->max, BSTR_P(param));
return M_OPT_OUT_OF_RANGE;
}
if (dst)
*(long long *)dst = tmp_int;
return 1;
}
static int clamp_int(const m_option_t *opt, void *val)
{
long long tmp = *(int *)val;
int r = clamp_longlong(opt, &tmp);
*(int *)val = tmp;
return r;
}
static int clamp_int64(const m_option_t *opt, void *val)
{
long long tmp = *(int64_t *)val;
int r = clamp_longlong(opt, &tmp);
*(int64_t *)val = tmp;
return r;
}
static int parse_int(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
long long tmp;
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
int r = parse_longlong(opt, name, param, &tmp);
if (r >= 0 && dst)
*(int *)dst = tmp;
return r;
}
static int parse_int64(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
long long tmp;
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
int r = parse_longlong(opt, name, param, &tmp);
if (r >= 0 && dst)
*(int64_t *)dst = tmp;
return r;
}
static char *print_int(const m_option_t *opt, const void *val)
{
if (opt->type->size == sizeof(int64_t))
return talloc_asprintf(NULL, "%"PRId64, *(const int64_t *)val);
return talloc_asprintf(NULL, "%d", *(const int *)val);
}
static void add_int64(const m_option_t *opt, void *val, double add, bool wrap)
{
int64_t v = *(int64_t *)val;
v = v + add;
bool is64 = opt->type->size == sizeof(int64_t);
int64_t nmin = is64 ? INT64_MIN : INT_MIN;
int64_t nmax = is64 ? INT64_MAX : INT_MAX;
int64_t min = (opt->flags & M_OPT_MIN) ? opt->min : nmin;
int64_t max = (opt->flags & M_OPT_MAX) ? opt->max : nmax;
if (v < min)
v = wrap ? max : min;
if (v > max)
v = wrap ? min : max;
*(int64_t *)val = v;
}
static void add_int(const m_option_t *opt, void *val, double add, bool wrap)
{
int64_t tmp = *(int *)val;
add_int64(opt, &tmp, add, wrap);
*(int *)val = tmp;
}
const m_option_type_t m_option_type_int = {
.name = "Integer",
.size = sizeof(int),
.parse = parse_int,
.print = print_int,
.copy = copy_opt,
.add = add_int,
.clamp = clamp_int,
};
const m_option_type_t m_option_type_int64 = {
.name = "Integer64",
.size = sizeof(int64_t),
.parse = parse_int64,
.print = print_int,
.copy = copy_opt,
.add = add_int64,
.clamp = clamp_int64,
};
static int parse_intpair(const struct m_option *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
if (param.len == 0)
return M_OPT_MISSING_PARAM;
struct bstr s = param;
int end = -1;
int start = bstrtoll(s, &s, 10);
if (s.len == param.len)
goto bad;
if (s.len > 0) {
if (!bstr_startswith0(s, "-"))
goto bad;
s = bstr_cut(s, 1);
}
if (s.len > 0)
end = bstrtoll(s, &s, 10);
if (s.len > 0)
goto bad;
if (dst) {
int *p = dst;
p[0] = start;
p[1] = end;
}
return 1;
bad:
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid integer range "
"specification for option %.*s: %.*s\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
}
const struct m_option_type m_option_type_intpair = {
.name = "Int[-Int]",
.size = sizeof(int[2]),
.parse = parse_intpair,
.copy = copy_opt,
};
static int clamp_choice(const m_option_t *opt, void *val)
{
int v = *(int *)val;
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
if (v >= opt->min && v <= opt->max)
return 0;
}
;
for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++) {
if (alt->value == v)
return 0;
}
return M_OPT_INVALID;
}
static int parse_choice(const struct m_option *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
struct m_opt_choice_alternatives *alt = opt->priv;
for ( ; alt->name; alt++)
if (!bstrcasecmp0(param, alt->name))
break;
if (!alt->name) {
if (param.len == 0)
return M_OPT_MISSING_PARAM;
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
long long val;
if (parse_longlong(opt, name, param, &val) == 1) {
if (dst)
*(int *)dst = val;
return 1;
}
}
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Invalid value for option %.*s: %.*s\n",
BSTR_P(name), BSTR_P(param));
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Valid values are:");
for (alt = opt->priv; alt->name; alt++)
mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", alt->name);
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX))
mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %g-%g", opt->min, opt->max);
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
return M_OPT_INVALID;
}
if (dst)
*(int *)dst = alt->value;
return 1;
}
static char *print_choice(const m_option_t *opt, const void *val)
{
int v = *(int *)val;
struct m_opt_choice_alternatives *alt;
for (alt = opt->priv; alt->name; alt++)
if (alt->value == v)
return talloc_strdup(NULL, alt->name);
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
if (v >= opt->min && v <= opt->max)
return talloc_asprintf(NULL, "%d", v);
}
abort();
}
static void choice_get_min_max(const struct m_option *opt, int *min, int *max)
{
assert(opt->type == &m_option_type_choice);
*min = INT_MAX;
*max = INT_MIN;
for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++) {
*min = FFMIN(*min, alt->value);
*max = FFMAX(*max, alt->value);
}
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
*min = FFMIN(*min, opt->min);
*max = FFMAX(*max, opt->max);
}
}
static void check_choice(int dir, int val, bool *found, int *best, int choice)
{
if ((dir == -1 && (!(*found) || choice > (*best)) && choice < val) ||
(dir == +1 && (!(*found) || choice < (*best)) && choice > val))
{
*found = true;
*best = choice;
}
}
static void add_choice(const m_option_t *opt, void *val, double add, bool wrap)
{
assert(opt->type == &m_option_type_choice);
int dir = add > 0 ? +1 : -1;
bool found = false;
int ival = *(int *)val;
int best = 0; // init. value unused
if (fabs(add) < 0.5)
return;
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
int newval = ival + add;
if (ival >= opt->min && ival <= opt->max &&
newval >= opt->min && newval <= opt->max)
{
found = true;
best = newval;
} else {
check_choice(dir, ival, &found, &best, opt->min);
check_choice(dir, ival, &found, &best, opt->max);
}
}
for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++)
check_choice(dir, ival, &found, &best, alt->value);
if (!found) {
int min, max;
choice_get_min_max(opt, &min, &max);
best = (dir == -1) ^ wrap ? min : max;
}
*(int *)val = best;
}
const struct m_option_type m_option_type_choice = {
.name = "String", // same as arbitrary strings in option list for now
.size = sizeof(int),
.parse = parse_choice,
.print = print_choice,
.copy = copy_opt,
.add = add_choice,
.clamp = clamp_choice,
};
// Float
#undef VAL
#define VAL(x) (*(double *)(x))
static int clamp_double(const m_option_t *opt, void *val)
{
double v = VAL(val);
int r = 0;
if ((opt->flags & M_OPT_MAX) && (v > opt->max)) {
v = opt->max;
r = M_OPT_OUT_OF_RANGE;
}
if ((opt->flags & M_OPT_MIN) && (v < opt->min)) {
v = opt->min;
r = M_OPT_OUT_OF_RANGE;
}
VAL(val) = v;
return r;
}
static int parse_double(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
if (param.len == 0)
return M_OPT_MISSING_PARAM;
struct bstr rest;
double tmp_float = bstrtod(param, &rest);
switch (rest.len ? rest.start[0] : 0) {
case ':':
case '/':
tmp_float /= bstrtod(bstr_cut(rest, 1), &rest);
break;
case '.':
case ',':
/* we also handle floats specified with
* non-locale decimal point ::atmos
*/
rest = bstr_cut(rest, 1);
if (tmp_float < 0)
tmp_float -= 1.0 / pow(10, rest.len) * bstrtod(rest, &rest);
else
tmp_float += 1.0 / pow(10, rest.len) * bstrtod(rest, &rest);
break;
}
if (rest.len) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"The %.*s option must be a floating point number or a "
"ratio (numerator[:/]denominator): %.*s\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
}
if (opt->flags & M_OPT_MIN)
if (tmp_float < opt->min) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"The %.*s option must be >= %f: %.*s\n",
BSTR_P(name), opt->min, BSTR_P(param));
return M_OPT_OUT_OF_RANGE;
}
if (opt->flags & M_OPT_MAX)
if (tmp_float > opt->max) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"The %.*s option must be <= %f: %.*s\n",
BSTR_P(name), opt->max, BSTR_P(param));
return M_OPT_OUT_OF_RANGE;
}
if (dst)
VAL(dst) = tmp_float;
return 1;
}
static char *print_double(const m_option_t *opt, const void *val)
{
return talloc_asprintf(NULL, "%f", VAL(val));
}
static char *print_double_f2(const m_option_t *opt, const void *val)
{
return talloc_asprintf(NULL, "%.2f", VAL(val));
}
static void add_double(const m_option_t *opt, void *val, double add, bool wrap)
{
double v = VAL(val);
v = v + add;
double min = (opt->flags & M_OPT_MIN) ? opt->min : -INFINITY;
double max = (opt->flags & M_OPT_MAX) ? opt->max : +INFINITY;
if (v < min)
v = wrap ? max : min;
if (v > max)
v = wrap ? min : max;
VAL(val) = v;
}
const m_option_type_t m_option_type_double = {
// double precision float or ratio (numerator[:/]denominator)
.name = "Double",
.size = sizeof(double),
.parse = parse_double,
.print = print_double,
.pretty_print = print_double_f2,
.copy = copy_opt,
.clamp = clamp_double,
};
#undef VAL
#define VAL(x) (*(float *)(x))
static int clamp_float(const m_option_t *opt, void *val)
{
double tmp = VAL(val);
int r = clamp_double(opt, &tmp);
VAL(val) = tmp;
return r;
}
static int parse_float(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
double tmp;
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
int r = parse_double(opt, name, param, &tmp);
if (r == 1 && dst)
VAL(dst) = tmp;
return r;
}
static char *print_float(const m_option_t *opt, const void *val)
{
return talloc_asprintf(NULL, "%f", VAL(val));
}
static char *print_float_f2(const m_option_t *opt, const void *val)
{
return talloc_asprintf(NULL, "%.2f", VAL(val));
}
static void add_float(const m_option_t *opt, void *val, double add, bool wrap)
{
double tmp = VAL(val);
add_double(opt, &tmp, add, wrap);
VAL(val) = tmp;
}
const m_option_type_t m_option_type_float = {
// floating point number or ratio (numerator[:/]denominator)
.name = "Float",
.size = sizeof(float),
.parse = parse_float,
.print = print_float,
.pretty_print = print_float_f2,
.copy = copy_opt,
.add = add_float,
.clamp = clamp_float,
};
///////////// String
#undef VAL
#define VAL(x) (*(char **)(x))
static char *unescape_string(void *talloc_ctx, bstr str)
{
char *res = talloc_strdup(talloc_ctx, "");
while (str.len) {
bstr rest;
bool esc = bstr_split_tok(str, "\\", &str, &rest);
res = talloc_strndup_append_buffer(res, str.start, str.len);
if (esc) {
if (!mp_parse_escape(&rest, &res)) {
talloc_free(res);
return NULL;
}
}
str = rest;
}
return res;
}
static char *escape_string(char *str0)
{
char *res = talloc_strdup(NULL, "");
bstr str = bstr0(str0);
while (str.len) {
bstr rest;
bool esc = bstr_split_tok(str, "\\", &str, &rest);
res = talloc_strndup_append_buffer(res, str.start, str.len);
if (esc)
res = talloc_strdup_append_buffer(res, "\\\\");
str = rest;
}
return res;
}
static int clamp_str(const m_option_t *opt, void *val)
{
char *v = VAL(val);
int len = v ? strlen(v) : 0;
if ((opt->flags & M_OPT_MIN) && (len < opt->min))
return M_OPT_OUT_OF_RANGE;
if ((opt->flags & M_OPT_MAX) && (len > opt->max))
return M_OPT_OUT_OF_RANGE;
return 0;
}
static int parse_str(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
int r = 1;
void *tmp = talloc_new(NULL);
if (param.start == NULL) {
r = M_OPT_MISSING_PARAM;
goto exit;
}
if (opt->flags & M_OPT_PARSE_ESCAPES) {
char *res = unescape_string(tmp, param);
if (!res) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Parameter has broken escapes: %.*s\n", BSTR_P(param));
r = M_OPT_INVALID;
goto exit;
}
param = bstr0(res);
}
if ((opt->flags & M_OPT_MIN) && (param.len < opt->min)) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Parameter must be >= %d chars: %.*s\n",
(int) opt->min, BSTR_P(param));
r = M_OPT_OUT_OF_RANGE;
goto exit;
}
if ((opt->flags & M_OPT_MAX) && (param.len > opt->max)) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Parameter must be <= %d chars: %.*s\n",
(int) opt->max, BSTR_P(param));
r = M_OPT_OUT_OF_RANGE;
goto exit;
}
if (dst) {
talloc_free(VAL(dst));
VAL(dst) = bstrdup0(NULL, param);
}
exit:
talloc_free(tmp);
return r;
}
static char *print_str(const m_option_t *opt, const void *val)
{
bool need_escape = opt->flags & M_OPT_PARSE_ESCAPES;
char *s = val ? VAL(val) : NULL;
return s ? (need_escape ? escape_string(s) : talloc_strdup(NULL, s)) : NULL;
}
static void copy_str(const m_option_t *opt, void *dst, const void *src)
{
if (dst && src) {
talloc_free(VAL(dst));
VAL(dst) = talloc_strdup(NULL, VAL(src));
}
}
static void free_str(void *src)
{
if (src && VAL(src)) {
talloc_free(VAL(src));
VAL(src) = NULL;
}
}
const m_option_type_t m_option_type_string = {
.name = "String",
.size = sizeof(char *),
.flags = M_OPT_TYPE_DYNAMIC,
.parse = parse_str,
.print = print_str,
.copy = copy_str,
.free = free_str,
.clamp = clamp_str,
};
//////////// String list
#undef VAL
#define VAL(x) (*(char ***)(x))
#define OP_NONE 0
#define OP_ADD 1
#define OP_PRE 2
#define OP_DEL 3
#define OP_CLR 4
static void free_str_list(void *dst)
{
char **d;
int i;
if (!dst || !VAL(dst))
return;
d = VAL(dst);
for (i = 0; d[i] != NULL; i++)
talloc_free(d[i]);
talloc_free(d);
VAL(dst) = NULL;
}
static int str_list_add(char **add, int n, void *dst, int pre)
{
if (!dst)
return M_OPT_PARSER_ERR;
char **lst = VAL(dst);
int ln;
for (ln = 0; lst && lst[ln]; ln++)
/**/;
lst = talloc_realloc(NULL, lst, char *, n + ln + 1);
if (pre) {
memmove(&lst[n], lst, ln * sizeof(char *));
memcpy(lst, add, n * sizeof(char *));
} else
memcpy(&lst[ln], add, n * sizeof(char *));
// (re-)add NULL-termination
lst[ln + n] = NULL;
talloc_free(add);
VAL(dst) = lst;
return 1;
}
static int str_list_del(char **del, int n, void *dst)
{
char **lst, *ep;
int i, ln, s;
long idx;
if (!dst)
return M_OPT_PARSER_ERR;
lst = VAL(dst);
for (ln = 0; lst && lst[ln]; ln++)
/**/;
s = ln;
for (i = 0; del[i] != NULL; i++) {
idx = strtol(del[i], &ep, 0);
if (*ep) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid index: %s\n", del[i]);
talloc_free(del[i]);
continue;
}
talloc_free(del[i]);
if (idx < 0 || idx >= ln) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Index %ld is out of range.\n", idx);
continue;
} else if (!lst[idx])
continue;
talloc_free(lst[idx]);
lst[idx] = NULL;
s--;
}
talloc_free(del);
if (s == 0) {
talloc_free(lst);
VAL(dst) = NULL;
return 1;
}
// Don't bother shrinking the list allocation
for (i = 0, n = 0; i < ln; i++) {
if (!lst[i])
continue;
lst[n] = lst[i];
n++;
}
lst[s] = NULL;
return 1;
}
static struct bstr get_nextsep(struct bstr *ptr, char sep, bool modify)
{
struct bstr str = *ptr;
struct bstr orig = str;
for (;;) {
int idx = bstrchr(str, sep);
if (idx > 0 && str.start[idx - 1] == '\\') {
if (modify) {
memmove(str.start + idx - 1, str.start + idx, str.len - idx);
str.len--;
str = bstr_cut(str, idx);
} else
str = bstr_cut(str, idx + 1);
} else {
str = bstr_cut(str, idx < 0 ? str.len : idx);
break;
}
}
*ptr = str;
return bstr_splice(orig, 0, str.start - orig.start);
}
static int parse_str_list(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
char **res;
int op = OP_NONE;
int len = strlen(opt->name);
if (opt->name[len - 1] == '*' && (name.len > len - 1)) {
struct bstr suffix = bstr_cut(name, len - 1);
if (bstrcasecmp0(suffix, "-add") == 0)
op = OP_ADD;
else if (bstrcasecmp0(suffix, "-pre") == 0)
op = OP_PRE;
else if (bstrcasecmp0(suffix, "-del") == 0)
op = OP_DEL;
else if (bstrcasecmp0(suffix, "-clr") == 0)
op = OP_CLR;
else
return M_OPT_UNKNOWN;
}
// Clear the list ??
if (op == OP_CLR) {
if (dst)
free_str_list(dst);
return 0;
}
// All other ops need a param
if (param.len == 0)
return M_OPT_MISSING_PARAM;
// custom type for "profile" calls this but uses ->priv for something else
char separator = opt->type == &m_option_type_string_list && opt->priv ?
*(char *)opt->priv : OPTION_LIST_SEPARATOR;
int n = 0;
struct bstr str = param;
while (str.len) {
get_nextsep(&str, separator, 0);
str = bstr_cut(str, 1);
n++;
}
if (n == 0)
return M_OPT_INVALID;
if (((opt->flags & M_OPT_MIN) && (n < opt->min)) ||
((opt->flags & M_OPT_MAX) && (n > opt->max)))
return M_OPT_OUT_OF_RANGE;
if (!dst)
return 1;
res = talloc_array(NULL, char *, n + 2);
str = bstrdup(NULL, param);
char *ptr = str.start;
n = 0;
while (1) {
struct bstr el = get_nextsep(&str, separator, 1);
res[n] = bstrdup0(NULL, el);
n++;
if (!str.len)
break;
str = bstr_cut(str, 1);
}
res[n] = NULL;
talloc_free(ptr);
switch (op) {
case OP_ADD:
return str_list_add(res, n, dst, 0);
case OP_PRE:
return str_list_add(res, n, dst, 1);
case OP_DEL:
return str_list_del(res, n, dst);
}
if (VAL(dst))
free_str_list(dst);
VAL(dst) = res;
return 1;
}
static void copy_str_list(const m_option_t *opt, void *dst, const void *src)
{
int n;
char **d, **s;
if (!(dst && src))
return;
s = VAL(src);
if (VAL(dst))
free_str_list(dst);
if (!s) {
VAL(dst) = NULL;
return;
}
for (n = 0; s[n] != NULL; n++)
/* NOTHING */;
d = talloc_array(NULL, char *, n + 1);
for (; n >= 0; n--)
d[n] = talloc_strdup(NULL, s[n]);
VAL(dst) = d;
}
static char *print_str_list(const m_option_t *opt, const void *src)
{
char **lst = NULL;
char *ret = NULL;
if (!(src && VAL(src)))
return NULL;
lst = VAL(src);
for (int i = 0; lst[i]; i++) {
if (ret)
ret = talloc_strdup_append_buffer(ret, ",");
ret = talloc_strdup_append_buffer(ret, lst[i]);
}
return ret;
}
const m_option_type_t m_option_type_string_list = {
/* A list of strings separated by ','.
* Option with a name ending in '*' permits using the following suffixes:
* -add: Add the given parameters at the end of the list.
* -pre: Add the given parameters at the beginning of the list.
* -del: Remove the entry at the given indices.
* -clr: Clear the list.
* e.g: -vf-add flip,mirror -vf-del 2,5
*/
.name = "String list",
.size = sizeof(char **),
.flags = M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
.parse = parse_str_list,
.print = print_str_list,
.copy = copy_str_list,
.free = free_str_list,
};
/////////////////// Print
static int parse_print(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
if (opt->type == CONF_TYPE_PRINT) {
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s", mp_gtext(opt->p));
} else {
char *name0 = bstrdup0(NULL, name);
char *param0 = bstrdup0(NULL, param);
int r = ((m_opt_func_full_t) opt->p)(opt, name0, param0);
talloc_free(name0);
talloc_free(param0);
return r;
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
}
if (opt->priv == NULL)
return M_OPT_EXIT;
return 0;
}
const m_option_type_t m_option_type_print = {
.name = "Print",
.flags = M_OPT_TYPE_OPTIONAL_PARAM,
.parse = parse_print,
};
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
const m_option_type_t m_option_type_print_func_param = {
.name = "Print",
.flags = M_OPT_TYPE_ALLOW_WILDCARD,
.parse = parse_print,
};
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
const m_option_type_t m_option_type_print_func = {
.name = "Print",
.flags = M_OPT_TYPE_ALLOW_WILDCARD | M_OPT_TYPE_OPTIONAL_PARAM,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
.parse = parse_print,
};
/////////////////////// Subconfig
#undef VAL
#define VAL(x) (*(char ***)(x))
static int parse_subconf(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
options: support parsing values into substructs Add an alternate mode for option parser objects (struct m_config) which is not inherently tied to any particular instance of an option value struct. Instead, this type or parsers can be used to initialize defaults in or parse values into a struct given as a parameter. They do not have the save slot functionality used for main player configuration. The new functionality will be used to replace the separate subopt_helper.c parsing code that is currently used to parse per-object suboptions in VOs etc. Previously, option default values were handled by initializing them in external code before creating a parser. This initialization was done with constants even for dynamically-allocated types like strings. Because trying to free a pointer to a constant would cause a crash when trying to replace the default with another value, parser initialization code then replaced all the original defaults with dynamically-allocated copies. This replace-with-copy behavior is no longer supported for new-style options; instead the option definition itself may contain a default value (new OPTDEF macros), and the new function m_config_initialize() is used to set all options to their default values. Convert the existing initialized dynamically allocated options in main config (the string options --dumpfile, --term-osd-esc, --input=conf) to use this. Other non-dynamic ones could be later converted to use this style of initialization too. There's currently no public call to free all dynamically allocated options in a given option struct because I intend to use talloc functionality for that (make them children of the struct and free with it).
2012-05-17 00:31:11 +00:00
int nr = 0;
char **lst = NULL;
if (param.len == 0)
return M_OPT_MISSING_PARAM;
struct bstr p = param;
while (p.len) {
int optlen = bstrcspn(p, ":=");
struct bstr subopt = bstr_splice(p, 0, optlen);
struct bstr subparam = bstr0(NULL);
p = bstr_cut(p, optlen);
if (bstr_startswith0(p, "=")) {
p = bstr_cut(p, 1);
if (bstr_startswith0(p, "\"")) {
p = bstr_cut(p, 1);
optlen = bstrcspn(p, "\"");
subparam = bstr_splice(p, 0, optlen);
p = bstr_cut(p, optlen);
if (!bstr_startswith0(p, "\"")) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Terminating '\"' missing for '%.*s'\n",
BSTR_P(subopt));
return M_OPT_INVALID;
}
p = bstr_cut(p, 1);
} else if (bstr_startswith0(p, "%")) {
p = bstr_cut(p, 1);
optlen = bstrtoll(p, &p, 0);
if (!bstr_startswith0(p, "%") || (optlen > p.len - 1)) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Invalid length %d for '%.*s'\n",
optlen, BSTR_P(subopt));
return M_OPT_INVALID;
}
subparam = bstr_splice(p, 1, optlen + 1);
p = bstr_cut(p, optlen + 1);
} else {
optlen = bstrcspn(p, ":");
subparam = bstr_splice(p, 0, optlen);
p = bstr_cut(p, optlen);
}
}
if (bstr_startswith0(p, ":"))
p = bstr_cut(p, 1);
else if (p.len > 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Incorrect termination for '%.*s'\n", BSTR_P(subopt));
return M_OPT_INVALID;
}
if (dst) {
lst = talloc_realloc(NULL, lst, char *, 2 * (nr + 2));
options: support parsing values into substructs Add an alternate mode for option parser objects (struct m_config) which is not inherently tied to any particular instance of an option value struct. Instead, this type or parsers can be used to initialize defaults in or parse values into a struct given as a parameter. They do not have the save slot functionality used for main player configuration. The new functionality will be used to replace the separate subopt_helper.c parsing code that is currently used to parse per-object suboptions in VOs etc. Previously, option default values were handled by initializing them in external code before creating a parser. This initialization was done with constants even for dynamically-allocated types like strings. Because trying to free a pointer to a constant would cause a crash when trying to replace the default with another value, parser initialization code then replaced all the original defaults with dynamically-allocated copies. This replace-with-copy behavior is no longer supported for new-style options; instead the option definition itself may contain a default value (new OPTDEF macros), and the new function m_config_initialize() is used to set all options to their default values. Convert the existing initialized dynamically allocated options in main config (the string options --dumpfile, --term-osd-esc, --input=conf) to use this. Other non-dynamic ones could be later converted to use this style of initialization too. There's currently no public call to free all dynamically allocated options in a given option struct because I intend to use talloc functionality for that (make them children of the struct and free with it).
2012-05-17 00:31:11 +00:00
lst[2 * nr] = bstrdup0(lst, subopt);
lst[2 * nr + 1] = bstrdup0(lst, subparam);
memset(&lst[2 * (nr + 1)], 0, 2 * sizeof(char *));
nr++;
}
}
if (dst)
VAL(dst) = lst;
return 1;
}
const m_option_type_t m_option_type_subconfig = {
// The syntax is -option opt1=foo:flag:opt2=blah
.name = "Subconfig",
.flags = M_OPT_TYPE_HAS_CHILD,
.parse = parse_subconf,
};
const m_option_type_t m_option_type_subconfig_struct = {
.name = "Subconfig",
.flags = M_OPT_TYPE_HAS_CHILD | M_OPT_TYPE_USE_SUBSTRUCT,
.parse = parse_subconf,
};
static int parse_color(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst)
{
if (param.len == 0)
return M_OPT_MISSING_PARAM;
bstr val = param;
struct m_color color = {0};
if (bstr_eatstart0(&val, "#")) {
// #[AA]RRGGBB
if (val.len != 6 && val.len != 8)
goto error;
bool has_alpha = val.len == 8;
uint32_t c = bstrtoll(val, &val, 16);
if (val.len)
goto error;
color = (struct m_color) {
(c >> 16) & 0xFF,
(c >> 8) & 0xFF,
c & 0xFF,
has_alpha ? (c >> 24) & 0xFF : 0xFF,
};
} else {
goto error;
}
if (dst)
*((struct m_color *)dst) = color;
return 1;
error:
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: invalid color: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Valid colors must be in the form #RRRGGBB or #AARRGGBB (in hex)\n");
return M_OPT_INVALID;
}
const m_option_type_t m_option_type_color = {
.name = "Color",
.size = sizeof(struct m_color),
.parse = parse_color,
};
// Parse a >=0 number starting at s. Set s to the string following the number.
// If the number ends with '%', eat that and set *out_per to true, but only
// if the number is between 0-100; if not, don't eat anything, even the number.
static bool eat_num_per(bstr *s, int *out_num, bool *out_per)
{
bstr rest;
long long v = bstrtoll(*s, &rest, 10);
if (s->len == rest.len || v < INT_MIN || v > INT_MAX)
return false;
*out_num = v;
*out_per = false;
*s = rest;
if (bstr_eatstart0(&rest, "%") && v >= 0 && v <= 100) {
*out_per = true;
*s = rest;
}
return true;
}
static bool parse_geometry_str(struct m_geometry *gm, bstr s)
{
*gm = (struct m_geometry) { .x = INT_MIN, .y = INT_MIN };
if (s.len == 0)
return true;
// Approximate grammar:
// [W[xH]][{+-}X{+-}Y] | [X:Y]
// (meaning: [optional] {one character of} one|alternative)
// Every number can be followed by '%'
int num;
bool per;
#define READ_NUM(F, F_PER) do { \
if (!eat_num_per(&s, &num, &per)) \
goto error; \
gm->F = num; \
gm->F_PER = per; \
} while(0)
#define READ_SIGN(F) do { \
if (bstr_eatstart0(&s, "+")) { \
gm->F = false; \
} else if (bstr_eatstart0(&s, "-")) {\
gm->F = true; \
} else goto error; \
} while(0)
if (bstrchr(s, ':') < 0) {
gm->wh_valid = true;
if (!bstr_startswith0(s, "+") && !bstr_startswith0(s, "-")) {
READ_NUM(w, w_per);
if (bstr_eatstart0(&s, "x"))
READ_NUM(h, h_per);
}
if (s.len > 0) {
gm->xy_valid = true;
READ_SIGN(x_sign);
READ_NUM(x, x_per);
READ_SIGN(y_sign);
READ_NUM(y, y_per);
}
} else {
gm->xy_valid = true;
READ_NUM(x, x_per);
if (!bstr_eatstart0(&s, ":"))
goto error;
READ_NUM(y, y_per);
}
return s.len == 0;
error:
return false;
}
#undef READ_NUM
#undef READ_SIGN
// xpos,ypos: position of the left upper corner
// widw,widh: width and height of the window
// scrw,scrh: width and height of the current screen
// The input parameters should be set to a centered window (default fallbacks).
void m_geometry_apply(int *xpos, int *ypos, int *widw, int *widh,
int scrw, int scrh, struct m_geometry *gm)
{
if (gm->wh_valid) {
int prew = *widw, preh = *widh;
if (gm->w > 0)
*widw = gm->w_per ? scrw * (gm->w / 100.0) : gm->w;
if (gm->h > 0)
*widh = gm->h_per ? scrh * (gm->h / 100.0) : gm->h;
// keep aspect if the other value is not set
double asp = (double)prew / preh;
if (gm->w > 0 && !(gm->h > 0)) {
*widh = *widw / asp;
} else if (!(gm->w > 0) && gm->h > 0) {
*widw = *widh * asp;
}
}
if (gm->xy_valid) {
if (gm->x != INT_MIN) {
*xpos = gm->x;
if (gm->x_per)
*xpos = (scrw - *widw) * (*xpos / 100.0);
if (gm->x_sign)
*xpos = scrw - *widw - *xpos;
}
if (gm->y != INT_MIN) {
*ypos = gm->y;
if (gm->y_per)
*ypos = (scrh - *widh) * (*ypos / 100.0);
if (gm->y_sign)
*ypos = scrh - *widh - *ypos;
}
}
}
static int parse_geometry(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst)
{
struct m_geometry gm;
if (!parse_geometry_str(&gm, param))
goto error;
if (dst)
*((struct m_geometry *)dst) = gm;
return 1;
error:
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: invalid geometry: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Valid format: [W[%%][xH[%%]]][{+-}X[%%]{+-}Y[%%]] | [X[%%]:Y[%%]]\n");
return M_OPT_INVALID;
}
const m_option_type_t m_option_type_geometry = {
.name = "Window geometry",
.size = sizeof(struct m_geometry),
.parse = parse_geometry,
};
static int parse_size_box(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst)
{
struct m_geometry gm;
if (!parse_geometry_str(&gm, param))
goto error;
if (gm.xy_valid)
goto error;
if (dst)
*((struct m_geometry *)dst) = gm;
return 1;
error:
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: invalid size: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Valid format: W[%%][xH[%%]] or empty string\n");
return M_OPT_INVALID;
}
const m_option_type_t m_option_type_size_box = {
.name = "Window size",
.size = sizeof(struct m_geometry),
.parse = parse_size_box,
};
#include "video/img_format.h"
static int parse_imgfmt(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
if (param.len == 0)
return M_OPT_MISSING_PARAM;
if (!bstrcmp0(param, "help")) {
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available formats:");
for (int i = 0; mp_imgfmt_list[i].name; i++)
mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %s", mp_imgfmt_list[i].name);
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
return M_OPT_EXIT - 1;
}
unsigned int fmt = mp_imgfmt_from_name(param, false);
if (!fmt) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: unknown format name: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
}
if (dst)
*((uint32_t *)dst) = fmt;
return 1;
}
const m_option_type_t m_option_type_imgfmt = {
// Please report any missing colorspaces
.name = "Image format",
.size = sizeof(uint32_t),
.parse = parse_imgfmt,
.copy = copy_opt,
};
video: decouple internal pixel formats from FourCCs mplayer's video chain traditionally used FourCCs for pixel formats. For example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the string 'YV12' interpreted as unsigned int. Additionally, it used to encode information into the numeric values of some formats. The RGB formats had their bit depth and endian encoded into the least significant byte. Extended planar formats (420P10 etc.) had chroma shift, endian, and component bit depth encoded. (This has been removed in recent commits.) Replace the FourCC mess with a simple enum. Remove all the redundant formats like YV12/I420/IYUV. Replace some image format names by something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P. Add img_fourcc.h, which contains the old IDs for code that actually uses FourCCs. Change the way demuxers, that output raw video, identify the video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to request the rawvideo decoder, and sh_video->imgfmt specifies the pixel format. Like the previous hack, this is supposed to avoid the need for a complete codecs.cfg entry per format, or other lookup tables. (Note that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT raw video, but this is still considered better than adding a raw video decoder - even if trivial, it would be full of annoying lookup tables.) The TV code has not been tested. Some corrective changes regarding endian and other image format flags creep in.
2012-12-23 19:03:30 +00:00
static int parse_fourcc(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst)
{
if (param.len == 0)
return M_OPT_MISSING_PARAM;
unsigned int value;
if (param.len == 4) {
uint8_t *s = param.start;
value = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
} else {
bstr rest;
value = bstrtoll(param, &rest, 16);
if (rest.len != 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: invalid FourCC: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
}
}
if (dst)
*((unsigned int *)dst) = value;
return 1;
}
const m_option_type_t m_option_type_fourcc = {
.name = "FourCC",
.size = sizeof(unsigned int),
.parse = parse_fourcc,
.copy = copy_opt,
};
#include "audio/format.h"
static int parse_afmt(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
if (param.len == 0)
return M_OPT_MISSING_PARAM;
if (!bstrcmp0(param, "help")) {
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available formats:");
for (int i = 0; af_fmtstr_table[i].name; i++)
mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %s", af_fmtstr_table[i].name);
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
return M_OPT_EXIT - 1;
}
int fmt = af_str2fmt_short(param);
if (fmt == -1) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: unknown format name: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
}
if (dst)
*((uint32_t *)dst) = fmt;
return 1;
}
const m_option_type_t m_option_type_afmt = {
// Please report any missing formats
.name = "Audio format",
.size = sizeof(uint32_t),
.parse = parse_afmt,
.copy = copy_opt,
};
static int parse_timestring(struct bstr str, double *time, char endchar)
{
int a, b, len;
double d;
*time = 0; /* ensure initialization for error cases */
if (bstr_sscanf(str, "%d:%d:%lf%n", &a, &b, &d, &len) >= 3)
*time = 3600 * a + 60 * b + d;
else if (bstr_sscanf(str, "%d:%lf%n", &a, &d, &len) >= 2)
*time = 60 * a + d;
else if (bstr_sscanf(str, "%lf%n", &d, &len) >= 1)
*time = d;
else
return 0; /* unsupported time format */
if (len < str.len && str.start[len] != endchar)
return 0; /* invalid extra characters at the end */
return len;
}
static int parse_time(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
double time;
if (param.len == 0)
return M_OPT_MISSING_PARAM;
if (!parse_timestring(param, &time, 0)) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: invalid time: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
}
if (dst)
*(double *)dst = time;
return 1;
}
static char *pretty_print_time(const m_option_t *opt, const void *val)
{
return mp_format_time(*(double *)val, false);
}
const m_option_type_t m_option_type_time = {
.name = "Time",
.size = sizeof(double),
.parse = parse_time,
.print = print_double,
.pretty_print = pretty_print_time,
.copy = copy_opt,
.add = add_double,
.clamp = clamp_double,
};
// Relative time
static int parse_rel_time(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst)
{
struct m_rel_time t = {0};
if (param.len == 0)
return M_OPT_MISSING_PARAM;
// Percent pos
if (bstr_endswith0(param, "%")) {
double percent = bstrtod(bstr_splice(param, 0, -1), &param);
if (param.len == 0 && percent >= 0 && percent <= 100) {
t.type = REL_TIME_PERCENT;
t.pos = percent;
goto out;
}
}
// Chapter pos
if (bstr_startswith0(param, "#")) {
int chapter = bstrtoll(bstr_cut(param, 1), &param, 10);
if (param.len == 0 && chapter >= 1) {
t.type = REL_TIME_CHAPTER;
t.pos = chapter - 1;
goto out;
}
}
bool sign = bstr_eatstart0(&param, "-");
double time;
if (parse_timestring(param, &time, 0)) {
t.type = sign ? REL_TIME_NEGATIVE : REL_TIME_ABSOLUTE;
t.pos = time;
goto out;
}
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: invalid time or position: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
out:
if (dst)
*(struct m_rel_time *)dst = t;
return 1;
}
const m_option_type_t m_option_type_rel_time = {
.name = "Relative time or percent position",
.size = sizeof(struct m_rel_time),
.parse = parse_rel_time,
.copy = copy_opt,
};
//// Objects (i.e. filters, etc) settings
#include "core/m_struct.h"
#undef VAL
#define VAL(x) (*(m_obj_settings_t **)(x))
static int find_obj_desc(struct bstr name, const m_obj_list_t *l,
const m_struct_t **ret)
{
int i;
char *n;
for (i = 0; l->list[i]; i++) {
n = M_ST_MB(char *, l->list[i], l->name_off);
if (!bstrcmp0(name, n)) {
*ret = M_ST_MB(m_struct_t *, l->list[i], l->desc_off);
return 1;
}
}
return 0;
}
static int get_obj_param(struct bstr opt_name, struct bstr obj_name,
const m_struct_t *desc, struct bstr str, int *nold,
int oldmax, char **dst)
{
const m_option_t *opt;
int r;
int eq = bstrchr(str, '=');
if (eq > 0) { // eq == 0 ignored
struct bstr p = bstr_cut(str, eq + 1);
str = bstr_splice(str, 0, eq);
opt = m_option_list_findb(desc->fields, str);
if (!opt) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: %.*s doesn't have a %.*s parameter.\n",
BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(str));
return M_OPT_UNKNOWN;
}
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
r = m_option_parse(opt, str, p, NULL);
if (r < 0) {
if (r > M_OPT_EXIT)
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: "
"Error while parsing %.*s parameter %.*s (%.*s)\n",
BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(str),
BSTR_P(p));
return r;
}
if (dst) {
dst[0] = bstrdup0(NULL, str);
dst[1] = bstrdup0(NULL, p);
}
} else {
if ((*nold) >= oldmax) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: %.*s has only %d params, so you can't give more than %d unnamed params.\n",
BSTR_P(opt_name), BSTR_P(obj_name), oldmax, oldmax);
return M_OPT_OUT_OF_RANGE;
}
opt = &desc->fields[(*nold)];
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
r = m_option_parse(opt, bstr0(opt->name), str, NULL);
if (r < 0) {
if (r > M_OPT_EXIT)
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: "
"Error while parsing %.*s parameter %s (%.*s)\n",
BSTR_P(opt_name), BSTR_P(obj_name), opt->name,
BSTR_P(str));
return r;
}
if (dst) {
dst[0] = talloc_strdup(NULL, opt->name);
dst[1] = bstrdup0(NULL, str);
}
(*nold)++;
}
return 1;
}
static int get_obj_params(struct bstr opt_name, struct bstr name,
struct bstr params, const m_struct_t *desc,
char separator, char ***_ret)
{
int n = 0, nold = 0, nopts;
char **ret;
if (!bstrcmp0(params, "help")) { // Help
char min[50], max[50];
if (!desc->fields) {
mp_msg(MSGT_CFGPARSER, MSGL_INFO,
"%.*s doesn't have any options.\n\n", BSTR_P(name));
return M_OPT_EXIT - 1;
}
mp_msg(MSGT_CFGPARSER, MSGL_INFO,
"\n Name Type Min Max\n\n");
for (n = 0; desc->fields[n].name; n++) {
const m_option_t *opt = &desc->fields[n];
if (opt->type->flags & M_OPT_TYPE_HAS_CHILD)
continue;
if (opt->flags & M_OPT_MIN)
sprintf(min, "%-8.0f", opt->min);
else
strcpy(min, "No");
if (opt->flags & M_OPT_MAX)
sprintf(max, "%-8.0f", opt->max);
else
strcpy(max, "No");
mp_msg(MSGT_CFGPARSER, MSGL_INFO,
" %-20.20s %-15.15s %-10.10s %-10.10s\n",
opt->name, opt->type->name, min, max);
}
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
return M_OPT_EXIT - 1;
}
for (nopts = 0; desc->fields[nopts].name; nopts++)
/* NOP */;
// TODO : Check that each opt can be parsed
struct bstr s = params;
while (1) {
bool end = false;
int idx = bstrchr(s, separator);
if (idx < 0) {
idx = s.len;
end = true;
}
struct bstr field = bstr_splice(s, 0, idx);
s = bstr_cut(s, idx + 1);
if (field.len == 0) { // Empty field, count it and go on
nold++;
} else {
int r = get_obj_param(opt_name, name, desc, field, &nold, nopts,
NULL);
if (r < 0)
return r;
n++;
}
if (end)
break;
}
if (nold > nopts) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Too many options for %.*s\n",
BSTR_P(name));
return M_OPT_OUT_OF_RANGE;
}
if (!_ret) // Just test
return 1;
if (n == 0) // No options or only empty options
return 1;
ret = talloc_array(NULL, char *, (n + 2) * 2);
n = nold = 0;
s = params;
while (s.len > 0) {
int idx = bstrchr(s, separator);
if (idx < 0)
idx = s.len;
struct bstr field = bstr_splice(s, 0, idx);
s = bstr_cut(s, idx + 1);
if (field.len == 0) { // Empty field, count it and go on
nold++;
} else {
get_obj_param(opt_name, name, desc, field, &nold, nopts,
&ret[n * 2]);
n++;
}
}
ret[n * 2] = ret[n * 2 + 1] = NULL;
*_ret = ret;
return 1;
}
static int parse_obj_params(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
char **opts;
int r;
m_obj_params_t *p = opt->priv;
const m_struct_t *desc;
// We need the object desc
if (!p)
return M_OPT_INVALID;
desc = p->desc;
r = get_obj_params(name, bstr0(desc->name), param, desc, p->separator,
dst ? &opts : NULL);
if (r < 0)
return r;
if (!dst)
return 1;
if (!opts) // no arguments given
return 1;
for (r = 0; opts[r]; r += 2)
m_struct_set(desc, dst, opts[r], bstr0(opts[r + 1]));
return 1;
}
const m_option_type_t m_option_type_obj_params = {
.name = "Object params",
.parse = parse_obj_params,
};
/// Some predefined types as a definition would be quite lengthy
/// Span arguments
static const m_span_t m_span_params_dflts = {
-1, -1
};
static const m_option_t m_span_params_fields[] = {
{"start", M_ST_OFF(m_span_t, start), CONF_TYPE_INT, M_OPT_MIN, 1, 0, NULL},
{"end", M_ST_OFF(m_span_t, end), CONF_TYPE_INT, M_OPT_MIN, 1, 0, NULL},
{ NULL, NULL, 0, 0, 0, 0, NULL }
};
static const struct m_struct_st m_span_opts = {
"m_span",
sizeof(m_span_t),
&m_span_params_dflts,
m_span_params_fields
};
const m_obj_params_t m_span_params_def = {
&m_span_opts,
'-'
};
static int parse_obj_settings(struct bstr opt, struct bstr str,
const m_obj_list_t *list,
m_obj_settings_t **_ret, int ret_n)
{
int r;
char **plist = NULL;
const m_struct_t *desc;
m_obj_settings_t *ret = _ret ? *_ret : NULL;
struct bstr param = bstr0(NULL);
int idx = bstrchr(str, '=');
if (idx >= 0) {
param = bstr_cut(str, idx + 1);
str = bstr_splice(str, 0, idx);
}
if (!find_obj_desc(str, list, &desc)) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: %.*s doesn't exist.\n",
BSTR_P(opt), BSTR_P(str));
return M_OPT_INVALID;
}
if (param.start) {
if (!desc && _ret) {
if (!bstrcmp0(param, "help")) {
mp_msg(MSGT_CFGPARSER, MSGL_INFO,
"Option %.*s: %.*s have no option description.\n",
BSTR_P(opt), BSTR_P(str));
return M_OPT_EXIT - 1;
}
plist = talloc_zero_array(NULL, char *, 4);
plist[0] = talloc_strdup(NULL, "_oldargs_");
plist[1] = bstrdup0(NULL, param);
} else if (desc) {
r = get_obj_params(opt, str, param, desc, ':',
_ret ? &plist : NULL);
if (r < 0)
return r;
}
}
if (!_ret)
return 1;
ret = talloc_realloc(NULL, ret, struct m_obj_settings, ret_n + 2);
memset(&ret[ret_n], 0, 2 * sizeof(m_obj_settings_t));
ret[ret_n].name = bstrdup0(NULL, str);
ret[ret_n].attribs = plist;
*_ret = ret;
return 1;
}
static int obj_settings_list_del(struct bstr opt_name, struct bstr param,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
void *dst)
{
char **str_list = NULL;
int r, i, idx_max = 0;
char *rem_id = "_removed_marker_";
char name[100];
assert(opt_name.len < 100);
memcpy(name, opt_name.start, opt_name.len);
name[opt_name.len] = 0;
const m_option_t list_opt = {
name, NULL, CONF_TYPE_STRING_LIST,
0, 0, 0, NULL
};
m_obj_settings_t *obj_list = dst ? VAL(dst) : NULL;
if (dst && !obj_list) {
mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %.*s: the list is empty.\n",
BSTR_P(opt_name));
return 1;
} else if (obj_list) {
for (idx_max = 0; obj_list[idx_max].name != NULL; idx_max++)
/* NOP */;
}
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
r = m_option_parse(&list_opt, opt_name, param, &str_list);
if (r < 0 || !str_list)
return r;
for (r = 0; str_list[r]; r++) {
int id;
char *endptr;
id = strtol(str_list[r], &endptr, 0);
if (endptr == str_list[r]) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: invalid parameter. We need a list of integers which are the indices of the elements to remove.\n", BSTR_P(opt_name));
m_option_free(&list_opt, &str_list);
return M_OPT_INVALID;
}
if (!obj_list)
continue;
if (id >= idx_max || id < -idx_max) {
mp_msg(MSGT_CFGPARSER, MSGL_WARN,
"Option %.*s: Index %d is out of range.\n",
BSTR_P(opt_name), id);
continue;
}
if (id < 0)
id = idx_max + id;
talloc_free(obj_list[id].name);
free_str_list(&(obj_list[id].attribs));
obj_list[id].name = rem_id;
}
if (!dst) {
m_option_free(&list_opt, &str_list);
return 1;
}
for (i = 0; obj_list[i].name; i++) {
while (obj_list[i].name == rem_id) {
memmove(&obj_list[i], &obj_list[i + 1],
sizeof(m_obj_settings_t) * (idx_max - i));
idx_max--;
}
}
obj_list = talloc_realloc(NULL, obj_list, struct m_obj_settings,
idx_max + 1);
VAL(dst) = obj_list;
return 1;
}
static void free_obj_settings_list(void *dst)
{
int n;
m_obj_settings_t *d;
if (!dst || !VAL(dst))
return;
d = VAL(dst);
for (n = 0; d[n].name; n++) {
talloc_free(d[n].name);
free_str_list(&(d[n].attribs));
}
talloc_free(d);
VAL(dst) = NULL;
}
static int parse_obj_settings_list(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr param, void *dst)
{
int len = strlen(opt->name);
m_obj_settings_t *res = NULL, *queue = NULL, *head = NULL;
int op = OP_NONE;
// We need the objects list
if (!opt->priv)
return M_OPT_INVALID;
if (opt->name[len - 1] == '*' && (name.len > len - 1)) {
struct bstr suffix = bstr_cut(name, len - 1);
if (bstrcasecmp0(suffix, "-add") == 0)
op = OP_ADD;
else if (bstrcasecmp0(suffix, "-pre") == 0)
op = OP_PRE;
else if (bstrcasecmp0(suffix, "-del") == 0)
op = OP_DEL;
else if (bstrcasecmp0(suffix, "-clr") == 0)
op = OP_CLR;
else {
char prefix[len];
strncpy(prefix, opt->name, len - 1);
prefix[len - 1] = '\0';
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: unknown postfix %.*s\n"
"Supported postfixes are:\n"
" %s-add\n"
" Append the given list to the current list\n\n"
" %s-pre\n"
" Prepend the given list to the current list\n\n"
" %s-del x,y,...\n"
" Remove the given elements. Take the list element index (starting from 0).\n"
" Negative index can be used (i.e. -1 is the last element)\n\n"
" %s-clr\n"
" Clear the current list.\n",
BSTR_P(name), BSTR_P(suffix), prefix, prefix, prefix, prefix);
return M_OPT_UNKNOWN;
}
}
// Clear the list ??
if (op == OP_CLR) {
if (dst)
free_obj_settings_list(dst);
return 0;
}
if (param.len == 0)
return M_OPT_MISSING_PARAM;
switch (op) {
case OP_ADD:
if (dst)
head = VAL(dst);
break;
case OP_PRE:
if (dst)
queue = VAL(dst);
break;
case OP_DEL:
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
return obj_settings_list_del(name, param, dst);
case OP_NONE:
if (dst && VAL(dst))
free_obj_settings_list(dst);
break;
default:
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: FIXME\n", BSTR_P(name));
return M_OPT_UNKNOWN;
}
if (!bstrcmp0(param, "help")) {
m_obj_list_t *ol = opt->priv;
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available video filters:\n");
for (int n = 0; ol->list[n]; n++)
mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-15s: %s\n",
M_ST_MB(char *, ol->list[n], ol->name_off),
M_ST_MB(char *, ol->list[n], ol->info_off));
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
return M_OPT_EXIT - 1;
}
struct bstr s = bstrdup(NULL, param);
char *allocptr = s.start;
int n = 0;
while (s.len > 0) {
struct bstr el = get_nextsep(&s, OPTION_LIST_SEPARATOR, 1);
int r = parse_obj_settings(name, el, opt->priv, dst ? &res : NULL, n);
if (r < 0) {
talloc_free(allocptr);
return r;
}
s = bstr_cut(s, 1);
n++;
}
talloc_free(allocptr);
if (n == 0)
return M_OPT_INVALID;
if (((opt->flags & M_OPT_MIN) && (n < opt->min)) ||
((opt->flags & M_OPT_MAX) && (n > opt->max)))
return M_OPT_OUT_OF_RANGE;
if (dst) {
if (queue) {
int qsize;
for (qsize = 0; queue[qsize].name; qsize++)
/* NOP */;
res = talloc_realloc(NULL, res, struct m_obj_settings,
qsize + n + 1);
memcpy(&res[n], queue, (qsize + 1) * sizeof(m_obj_settings_t));
n += qsize;
talloc_free(queue);
}
if (head) {
int hsize;
for (hsize = 0; head[hsize].name; hsize++)
/* NOP */;
head = talloc_realloc(NULL, head, struct m_obj_settings,
hsize + n + 1);
memcpy(&head[hsize], res, (n + 1) * sizeof(m_obj_settings_t));
talloc_free(res);
res = head;
}
VAL(dst) = res;
}
return 1;
}
static void copy_obj_settings_list(const m_option_t *opt, void *dst,
const void *src)
{
m_obj_settings_t *d, *s;
int n;
if (!(dst && src))
return;
s = VAL(src);
if (VAL(dst))
free_obj_settings_list(dst);
if (!s)
return;
for (n = 0; s[n].name; n++)
/* NOP */;
d = talloc_array(NULL, struct m_obj_settings, n + 1);
for (n = 0; s[n].name; n++) {
d[n].name = talloc_strdup(NULL, s[n].name);
d[n].attribs = NULL;
copy_str_list(NULL, &(d[n].attribs), &(s[n].attribs));
}
d[n].name = NULL;
d[n].attribs = NULL;
VAL(dst) = d;
}
const m_option_type_t m_option_type_obj_settings_list = {
.name = "Object settings list",
.size = sizeof(m_obj_settings_t *),
.flags = M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
.parse = parse_obj_settings_list,
.copy = copy_obj_settings_list,
.free = free_obj_settings_list,
};
static int parse_custom_url(const m_option_t *opt, struct bstr name,
options: get rid of ambiguous option parsing Options parsing used to be ambiguous, as in the splitting into option and values pairs was ambiguous. Example: -option -something It wasn't clear whether -option actually takes an argument or not. The string "-something" could either be a separate option, or an argument to "-option". The code had to call the option specific parser function to resolve this. This made everything complicated and didn't even have a real use. There was only one case where this was actually used: string lists (m_option_type_string_list) and options based on it. That is because this option type actually turns a single option into a proxy for several real arguments, e.g. "vf*" can handle "-vf-add" and "-vf-clr". Options suffixed with "-clr" are the only options of this group which take no arguments. This is ambiguous only with the "old syntax" (as shown above). The "new" option syntax always puts option name and value into same argument. (E.g. "--option=--something" or "--option" "--something".) Simplify the code by making it statically known whether an option takes a parameter or not with the flag M_OPT_TYPE_OLD_SYNTAX_NO_PARAM. If it's set, the option parser assumes the option takes no argument. The only real ambiguity left, string list options that end on "-clr", are special cased in the parser. Remove some duplication of the logic in the command line parser by moving all argument splitting logic into split_opt(). (It's arguable whether that can be considered code duplication, but now the code is a bit simpler anyway. This might be subjective.) Remove the "ambiguous" parameter from all option parsing related code. Make m_config unaware of the pre-parsing concept. Make most CONF_NOCFG options also CONF_GLOBAL (except those explicitly usable as per-file options.)
2012-08-05 21:34:28 +00:00
struct bstr url, void *dst)
{
int r;
m_struct_t *desc = opt->priv;
if (!desc) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: Custom URL needs "
"a pointer to a m_struct_t in the priv field.\n", BSTR_P(name));
return M_OPT_PARSER_ERR;
}
// extract the protocol
int idx = bstr_find0(url, "://");
if (idx < 0) {
// Filename only
if (m_option_list_find(desc->fields, "filename")) {
m_struct_set(desc, dst, "filename", url);
return 1;
}
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: URL doesn't have a valid protocol!\n",
BSTR_P(name));
return M_OPT_INVALID;
}
struct bstr ptr1 = bstr_cut(url, idx + 3);
if (m_option_list_find(desc->fields, "string")) {
if (ptr1.len > 0) {
m_struct_set(desc, dst, "string", ptr1);
return 1;
}
}
if (dst && m_option_list_find(desc->fields, "protocol")) {
r = m_struct_set(desc, dst, "protocol", bstr_splice(url, 0, idx));
if (r < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: Error while setting protocol.\n",
BSTR_P(name));
return r;
}
}
// check if a username:password is given
idx = bstrchr(ptr1, '/');
if (idx < 0)
idx = ptr1.len;
struct bstr hostpart = bstr_splice(ptr1, 0, idx);
struct bstr path = bstr_cut(ptr1, idx);
idx = bstrchr(hostpart, '@');
if (idx >= 0) {
struct bstr userpass = bstr_splice(hostpart, 0, idx);
hostpart = bstr_cut(hostpart, idx + 1);
// We got something, at least a username...
if (!m_option_list_find(desc->fields, "username")) {
mp_msg(MSGT_CFGPARSER, MSGL_WARN,
"Option %.*s: This URL doesn't have a username part.\n",
BSTR_P(name));
// skip
} else {
idx = bstrchr(userpass, ':');
if (idx >= 0) {
// We also have a password
if (!m_option_list_find(desc->fields, "password")) {
mp_msg(MSGT_CFGPARSER, MSGL_WARN,
"Option %.*s: This URL doesn't have a password part.\n",
BSTR_P(name));
// skip
} else { // Username and password
if (dst) {
r = m_struct_set(desc, dst, "username",
bstr_splice(userpass, 0, idx));
if (r < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: Error while setting username.\n",
BSTR_P(name));
return r;
}
r = m_struct_set(desc, dst, "password",
bstr_splice(userpass, idx+1,
userpass.len));
if (r < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: Error while setting password.\n",
BSTR_P(name));
return r;
}
}
}
} else { // User name only
r = m_struct_set(desc, dst, "username", userpass);
if (r < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: Error while setting username.\n",
BSTR_P(name));
return r;
}
}
}
}
// Before looking for a port number check if we have an IPv6 type
// numeric address.
// In an IPv6 URL the numeric address should be inside square braces.
int idx1 = bstrchr(hostpart, '[');
int idx2 = bstrchr(hostpart, ']');
struct bstr portstr = hostpart;
bool v6addr = false;
if (idx1 >= 0 && idx2 >= 0 && idx1 < idx2) {
// we have an IPv6 numeric address
portstr = bstr_cut(hostpart, idx2);
hostpart = bstr_splice(hostpart, idx1 + 1, idx2);
v6addr = true;
}
idx = bstrchr(portstr, ':');
if (idx >= 0) {
if (!v6addr)
hostpart = bstr_splice(hostpart, 0, idx);
// We have an URL beginning like http://www.hostname.com:1212
// Get the port number
if (!m_option_list_find(desc->fields, "port")) {
mp_msg(MSGT_CFGPARSER, MSGL_WARN,
"Option %.*s: This URL doesn't have a port part.\n",
BSTR_P(name));
// skip
} else {
if (dst) {
int p = bstrtoll(bstr_cut(portstr, idx + 1), NULL, 0);
char tmp[100];
snprintf(tmp, 99, "%d", p);
r = m_struct_set(desc, dst, "port", bstr0(tmp));
if (r < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: Error while setting port.\n",
BSTR_P(name));
return r;
}
}
}
}
// Get the hostname
if (hostpart.len > 0) {
if (!m_option_list_find(desc->fields, "hostname")) {
mp_msg(MSGT_CFGPARSER, MSGL_WARN,
"Option %.*s: This URL doesn't have a hostname part.\n",
BSTR_P(name));
// skip
} else {
r = m_struct_set(desc, dst, "hostname", hostpart);
if (r < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: Error while setting hostname.\n",
BSTR_P(name));
return r;
}
}
}
// Look if a path is given
if (path.len > 1) { // not just "/"
// copy the path/filename in the URL container
if (!m_option_list_find(desc->fields, "filename")) {
mp_msg(MSGT_CFGPARSER, MSGL_WARN,
"Option %.*s: This URL doesn't have a filename part.\n",
BSTR_P(name));
// skip
} else {
if (dst) {
char *fname = bstrdup0(NULL, bstr_cut(path, 1));
url_unescape_string(fname, fname);
r = m_struct_set(desc, dst, "filename", bstr0(fname));
talloc_free(fname);
if (r < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Option %.*s: Error while setting filename.\n",
BSTR_P(name));
return r;
}
}
}
}
return 1;
}
/// TODO : Write the other needed funcs for 'normal' options
const m_option_type_t m_option_type_custom_url = {
.name = "Custom URL",
.parse = parse_custom_url,
};