diff options
Diffstat (limited to 'core/m_option.c')
-rw-r--r-- | core/m_option.c | 438 |
1 files changed, 212 insertions, 226 deletions
diff --git a/core/m_option.c b/core/m_option.c index 6d011ee141..a667471407 100644 --- a/core/m_option.c +++ b/core/m_option.c @@ -59,6 +59,14 @@ char *m_option_strerror(int code) } } +int m_option_required_params(const m_option_t *opt) +{ + if (((opt->flags & M_OPT_OPTIONAL_PARAM) || + (opt->type->flags & M_OPT_TYPE_OPTIONAL_PARAM))) + return 0; + return 1; +} + static const struct m_option *m_option_list_findb(const struct m_option *list, struct bstr name) { @@ -611,9 +619,9 @@ 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) +static char *print_double_f3(const m_option_t *opt, const void *val) { - return talloc_asprintf(NULL, "%.2f", VAL(val)); + return talloc_asprintf(NULL, "%.3f", VAL(val)); } static void add_double(const m_option_t *opt, void *val, double add, bool wrap) @@ -639,7 +647,7 @@ const m_option_type_t m_option_type_double = { .size = sizeof(double), .parse = parse_double, .print = print_double, - .pretty_print = print_double_f2, + .pretty_print = print_double_f3, .copy = copy_opt, .clamp = clamp_double, }; @@ -670,9 +678,9 @@ 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) +static char *print_float_f3(const m_option_t *opt, const void *val) { - return talloc_asprintf(NULL, "%.2f", VAL(val)); + return talloc_asprintf(NULL, "%.3f", VAL(val)); } static void add_float(const m_option_t *opt, void *val, double add, bool wrap) @@ -688,7 +696,7 @@ const m_option_type_t m_option_type_float = { .size = sizeof(float), .parse = parse_float, .print = print_float, - .pretty_print = print_float_f2, + .pretty_print = print_float_f3, .copy = copy_opt, .add = add_float, .clamp = clamp_float, @@ -1140,6 +1148,78 @@ const m_option_type_t m_option_type_print_func = { #undef VAL #define VAL(x) (*(char ***)(x)) +// Read s sub-option name, or a positional sub-opt value. +// Return 0 on succes, M_OPT_ error code otherwise. +// optname is for error reporting. +static int read_subparam(bstr optname, bstr *str, bstr *out_subparam) +{ + bstr p = *str; + bstr subparam = {0}; + + if (bstr_eatstart0(&p, "\"")) { + int 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(optname)); + return M_OPT_INVALID; + } + p = bstr_cut(p, 1); + } else if (bstr_eatstart0(&p, "[")) { + if (!bstr_split_tok(p, "]", &subparam, &p)) { + mp_msg(MSGT_CFGPARSER, MSGL_ERR, + "Terminating ']' missing for '%.*s'\n", + BSTR_P(optname)); + return M_OPT_INVALID; + } + } else if (bstr_eatstart0(&p, "%")) { + int 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(optname)); + return M_OPT_INVALID; + } + subparam = bstr_splice(p, 1, optlen + 1); + p = bstr_cut(p, optlen + 1); + } else { + // Skip until the next character that could possibly be a meta + // character in option parsing. + int optlen = bstrcspn(p, ":=,\\%\"'[]"); + subparam = bstr_splice(p, 0, optlen); + p = bstr_cut(p, optlen); + } + + *str = p; + *out_subparam = subparam; + return 0; +} + +// Return 0 on success, otherwise error code +// On success, set *out_name and *out_val, and advance *str +// out_val.start is NULL if there was no parameter. +// optname is for error reporting. +static int split_subconf(bstr optname, bstr *str, bstr *out_name, bstr *out_val) +{ + bstr p = *str; + bstr subparam = {0}; + bstr subopt; + int r = read_subparam(optname, &p, &subopt); + if (r < 0) + return r; + if (bstr_eatstart0(&p, "=")) { + r = read_subparam(subopt, &p, &subparam); + if (r < 0) + return r; + } + *str = p; + *out_name = subopt; + *out_val = subparam; + return 0; +} + static int parse_subconf(const m_option_t *opt, struct bstr name, struct bstr param, void *dst) { @@ -1152,41 +1232,10 @@ static int parse_subconf(const m_option_t *opt, struct bstr name, 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); - } - } + bstr subopt, subparam; + int r = split_subconf(name, &p, &subopt, &subparam); + if (r < 0) + return r; if (bstr_startswith0(p, ":")) p = bstr_cut(p, 1); else if (p.len > 0) { @@ -1197,8 +1246,8 @@ static int parse_subconf(const m_option_t *opt, struct bstr name, if (dst) { lst = talloc_realloc(NULL, lst, char *, 2 * (nr + 2)); - lst[2 * nr] = bstrdup0(lst, subopt); - lst[2 * nr + 1] = bstrdup0(lst, subparam); + lst[2 * nr] = bstrto0(lst, subopt); + lst[2 * nr + 1] = bstrto0(lst, subparam); memset(&lst[2 * (nr + 1)], 0, 2 * sizeof(char *)); nr++; } @@ -1693,216 +1742,151 @@ static int find_obj_desc(struct bstr name, const m_obj_list_t *l, 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) +// Consider -vf a=b=c:d=e. This verifies "b"="c" and "d"="e" and that the +// option names/values are correct. Try to determine whether an option +// without '=' sets a flag, or whether it's a positional argument. +static int get_obj_param(bstr opt_name, bstr obj_name, const m_struct_t *desc, + bstr name, bstr val, int *nold, int oldmax, + bstr *out_name, bstr *out_val) { - const m_option_t *opt; + const m_option_t *opt = m_option_list_findb(desc->fields, name); 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); + // va.start != NULL => of the form name=val (not positional) + // If it's just "name", and the associated option exists and is a flag, + // don't accept it as positional argument. + if (val.start || (opt && m_option_required_params(opt) == 0)) { 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)); + BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(name)); return M_OPT_UNKNOWN; } - r = m_option_parse(opt, str, p, NULL); + r = m_option_parse(opt, name, val, 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)); + BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(name), + BSTR_P(val)); return r; } - if (dst) { - dst[0] = bstrdup0(NULL, str); - dst[1] = bstrdup0(NULL, p); - } + *out_name = name; + *out_val = val; + return 1; } else { + val = name; + // positional fields + if (val.len == 0) { // Empty field, count it and go on + (*nold)++; + return 0; + } 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)]; - r = m_option_parse(opt, bstr0(opt->name), str, NULL); + r = m_option_parse(opt, bstr0(opt->name), val, 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)); + BSTR_P(val)); return r; } - if (dst) { - dst[0] = talloc_strdup(NULL, opt->name); - dst[1] = bstrdup0(NULL, str); - } + *out_name = bstr0(opt->name); + *out_val = val; (*nold)++; + return 1; } - return 1; } +// Consider -vf a=b:c:d. This parses "b:c:d" into name/value pairs, stored as +// linear array in *_ret. In particular, desc contains what options a the +// object takes, and verifies the option values as well. static int get_obj_params(struct bstr opt_name, struct bstr name, - struct bstr params, const m_struct_t *desc, - char separator, char ***_ret) + struct bstr *pstr, const m_struct_t *desc, + 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; - } + char **args = NULL; + int num_args = 0; + int r = 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++; + while (pstr->len > 0) { + bstr fname, fval; + r = split_subconf(opt_name, pstr, &fname, &fval); + if (r < 0) + goto exit; + if (bstr_equals0(fname, "help")) + goto print_help; + r = get_obj_param(opt_name, name, desc, fname, fval, &nold, nopts, + &fname, &fval); + if (r < 0) + goto exit; + + if (r > 0 && ret) { + MP_TARRAY_APPEND(NULL, args, num_args, bstrto0(NULL, fname)); + MP_TARRAY_APPEND(NULL, args, num_args, bstrto0(NULL, fval)); } - if (end) + + if (!bstr_eatstart0(pstr, ":")) 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++; + if (ret) { + if (num_args > 0) { + for (int n = 0; n < 2; n++) + MP_TARRAY_APPEND(NULL, args, num_args, NULL); + *ret = args; + args = NULL; } else { - get_obj_param(opt_name, name, desc, field, &nold, nopts, - &ret[n * 2]); - n++; + *ret = NULL; } } - 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, - 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])); +exit: + return r; - return 1; +print_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; } +// Characters which may appear in a filter name +#define NAMECH "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" -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, +// Parse one item, e.g. -vf a=b:c:d,e=f:g => parse a=b:c:d into "a" and "b:c:d" +static int parse_obj_settings(struct bstr opt, struct bstr *pstr, const m_obj_list_t *list, m_obj_settings_t **_ret, int ret_n) { @@ -1911,12 +1895,12 @@ static int parse_obj_settings(struct bstr opt, struct bstr str, 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); - } + bool has_param = false; + int idx = bstrspn(*pstr, NAMECH); + bstr str = bstr_splice(*pstr, 0, idx); + *pstr = bstr_cut(*pstr, idx); + if (bstr_eatstart0(pstr, "=")) + has_param = true; if (!find_obj_desc(str, list, &desc)) { mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: %.*s doesn't exist.\n", @@ -1924,20 +1908,26 @@ static int parse_obj_settings(struct bstr opt, struct bstr str, return M_OPT_INVALID; } - if (param.start) { - if (!desc && _ret) { + if (has_param) { + if (!desc) { + // Should perhaps be parsed as escape-able string. But this is a + // compatibility path, so it's not worth the trouble. + int next = bstrcspn(*pstr, ","); + bstr param = bstr_splice(*pstr, 0, next); + *pstr = bstr_cut(*pstr, next); 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); + if (_ret) { + plist = talloc_zero_array(NULL, char *, 4); + plist[0] = talloc_strdup(NULL, "_oldargs_"); + plist[1] = bstrto0(NULL, param); + } } else if (desc) { - r = get_obj_params(opt, str, param, desc, ':', - _ret ? &plist : NULL); + r = get_obj_params(opt, str, pstr, desc, _ret ? &plist : NULL); if (r < 0) return r; } @@ -1947,7 +1937,7 @@ static int parse_obj_settings(struct bstr opt, struct bstr str, 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].name = bstrto0(NULL, str); ret[ret_n].attribs = plist; *_ret = ret; @@ -2127,20 +2117,16 @@ static int parse_obj_settings_list(const m_option_t *opt, struct bstr name, 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); + while (param.len > 0) { + int r = parse_obj_settings(name, ¶m, opt->priv, dst ? &res : NULL, n); + if (r < 0) return r; - } - s = bstr_cut(s, 1); + char sep[2] = {OPTION_LIST_SEPARATOR, 0}; + if (param.len > 0 && !bstr_eatstart0(¶m, sep)) + return M_OPT_INVALID; n++; } - talloc_free(allocptr); if (n == 0) return M_OPT_INVALID; |