/* * 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 Properties #include "config.h" #include #include #include #include #include #include #include "talloc.h" #include "mpvcore/m_option.h" #include "m_property.h" #include "mpvcore/mp_msg.h" #include "mpvcore/mp_common.h" const struct m_option_type m_option_type_dummy = { .name = "Unknown", .flags = M_OPT_TYPE_ALLOW_WILDCARD, // make "vf*" property work }; struct legacy_prop { const char *old, *new; }; static const struct legacy_prop legacy_props[] = { {"switch_video", "video"}, {"switch_audio", "audio"}, {"switch_program", "program"}, {"framedropping", "framedrop"}, {"osdlevel", "osd-level"}, {0} }; static bool translate_legacy_property(const char *name, char *buffer, size_t buffer_size) { if (strlen(name) + 1 > buffer_size) return false; const char *old_name = name; for (int n = 0; legacy_props[n].new; n++) { if (strcmp(name, legacy_props[n].old) == 0) { name = legacy_props[n].new; break; } } snprintf(buffer, buffer_size, "%s", name); // Old names used "_" instead of "-" for (int n = 0; buffer[n]; n++) { if (buffer[n] == '_') buffer[n] = '-'; } if (strcmp(old_name, buffer) != 0) { mp_msg(MSGT_CPLAYER, MSGL_WARN, "Warning: property '%s' is deprecated, " "replaced with '%s'. Fix your input.conf!\n", old_name, buffer); } return true; } static int do_action(const m_option_t *prop_list, const char *name, int action, void *arg, void *ctx) { const char *sep; const m_option_t *prop; struct m_property_action_arg ka; if ((sep = strchr(name, '/')) && sep[1]) { int len = sep - name; char base[len + 1]; memcpy(base, name, len); base[len] = 0; prop = m_option_list_find(prop_list, base); ka = (struct m_property_action_arg) { .key = sep + 1, .action = action, .arg = arg, }; action = M_PROPERTY_KEY_ACTION; arg = &ka; } else prop = m_option_list_find(prop_list, name); if (!prop) return M_PROPERTY_UNKNOWN; int (*control)(const m_option_t*, int, void*, void*) = prop->p; int r = control(prop, action, arg, ctx); if (action == M_PROPERTY_GET_TYPE && r < 0 && prop->type != &m_option_type_dummy) { *(struct m_option *)arg = *prop; return M_PROPERTY_OK; } return r; } int m_property_do(const m_option_t *prop_list, const char *in_name, int action, void *arg, void *ctx) { union m_option_value val = {0}; int r; char name[64]; if (!translate_legacy_property(in_name, name, sizeof(name))) return M_PROPERTY_UNKNOWN; struct m_option opt = {0}; r = do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx); if (r <= 0) return r; assert(opt.type); switch (action) { case M_PROPERTY_PRINT: { if ((r = do_action(prop_list, name, M_PROPERTY_PRINT, arg, ctx)) >= 0) return r; // Fallback to m_option if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0) return r; char *str = m_option_pretty_print(&opt, &val); m_option_free(&opt, &val); *(char **)arg = str; return str != NULL; } case M_PROPERTY_GET_STRING: { if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0) return r; char *str = m_option_print(&opt, &val); m_option_free(&opt, &val); *(char **)arg = str; return str != NULL; } case M_PROPERTY_SET_STRING: { // (reject 0 return value: success, but empty string with flag) if (m_option_parse(&opt, bstr0(name), bstr0(arg), &val) <= 0) return M_PROPERTY_ERROR; r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx); m_option_free(&opt, &val); return r; } case M_PROPERTY_SWITCH: { struct m_property_switch_arg *sarg = arg; if ((r = do_action(prop_list, name, M_PROPERTY_SWITCH, arg, ctx)) != M_PROPERTY_NOT_IMPLEMENTED) return r; // Fallback to m_option if (!opt.type->add) return M_PROPERTY_NOT_IMPLEMENTED; if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0) return r; opt.type->add(&opt, &val, sarg->inc, sarg->wrap); r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx); m_option_free(&opt, &val); return r; } case M_PROPERTY_SET: { if (!opt.type->clamp) { mp_msg(MSGT_CPLAYER, MSGL_WARN, "Property '%s' without clamp().\n", name); } else { m_option_copy(&opt, &val, arg); r = opt.type->clamp(&opt, arg); m_option_free(&opt, &val); if (r != 0) { mp_msg(MSGT_CPLAYER, MSGL_ERR, "Property '%s': invalid value.\n", name); return M_PROPERTY_ERROR; } } return do_action(prop_list, name, M_PROPERTY_SET, arg, ctx); } default: return do_action(prop_list, name, action, arg, ctx); } } static int m_property_do_bstr(const m_option_t *prop_list, bstr name, int action, void *arg, void *ctx) { char name0[64]; if (name.len >= sizeof(name0)) return M_PROPERTY_UNKNOWN; snprintf(name0, sizeof(name0), "%.*s", BSTR_P(name)); return m_property_do(prop_list, name0, action, arg, ctx); } static void append_str(char **s, int *len, bstr append) { MP_TARRAY_GROW(NULL, *s, *len + append.len); memcpy(*s + *len, append.start, append.len); *len = *len + append.len; } static int expand_property(const m_option_t *prop_list, char **ret, int *ret_len, bstr prop, bool silent_error, void *ctx) { bool cond_yes = bstr_eatstart0(&prop, "?"); bool cond_no = !cond_yes && bstr_eatstart0(&prop, "!"); bool test = cond_yes || cond_no; bool raw = bstr_eatstart0(&prop, "="); bstr comp_with = {0}; bool comp = test && bstr_split_tok(prop, "==", &prop, &comp_with); if (test && !comp) raw = true; int method = raw ? M_PROPERTY_GET_STRING : M_PROPERTY_PRINT; char *s = NULL; int r = m_property_do_bstr(prop_list, prop, method, &s, ctx); bool skip; if (comp) { skip = ((s && bstr_equals0(comp_with, s)) != cond_yes); } else if (test) { skip = (!!s != cond_yes); } else { skip = !!s; char *append = s; if (!s && !silent_error && !raw) append = (r == M_PROPERTY_UNAVAILABLE) ? "(unavailable)" : "(error)"; append_str(ret, ret_len, bstr0(append)); } talloc_free(s); return skip; } char *m_properties_expand_string(const m_option_t *prop_list, char *str0, void *ctx) { char *ret = NULL; int ret_len = 0; bool skip = false; int level = 0, skip_level = 0; bstr str = bstr0(str0); while (str.len) { if (level > 0 && bstr_eatstart0(&str, "}")) { if (skip && level <= skip_level) skip = false; level--; } else if (bstr_startswith0(str, "${") && bstr_find0(str, "}") >= 0) { str = bstr_cut(str, 2); level++; // Assume ":" and "}" can't be part of the property name // => if ":" comes before "}", it must be for the fallback int term_pos = bstrcspn(str, ":}"); bstr name = bstr_splice(str, 0, term_pos < 0 ? str.len : term_pos); str = bstr_cut(str, term_pos); bool have_fallback = bstr_eatstart0(&str, ":"); if (!skip) { skip = expand_property(prop_list, &ret, &ret_len, name, have_fallback, ctx); if (skip) skip_level = level; } } else if (level == 0 && bstr_eatstart0(&str, "$>")) { append_str(&ret, &ret_len, str); break; } else { char c; // Other combinations, e.g. "$x", are added verbatim if (bstr_eatstart0(&str, "$$")) { c = '$'; } else if (bstr_eatstart0(&str, "$}")) { c = '}'; } else { c = str.start[0]; str = bstr_cut(str, 1); } if (!skip) MP_TARRAY_APPEND(NULL, ret, ret_len, c); } } MP_TARRAY_APPEND(NULL, ret, ret_len, '\0'); return ret; } void m_properties_print_help_list(const m_option_t *list) { char min[50], max[50]; int i, count = 0; mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\n Name Type Min Max\n\n"); for (i = 0; list[i].name; i++) { const m_option_t *opt = &list[i]; 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); count++; } mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\nTotal: %d properties\n", count); } int m_property_int_ro(const m_option_t *prop, int action, void *arg, int var) { if (action == M_PROPERTY_GET) { *(int *)arg = var; return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; } int m_property_int64_ro(const struct m_option* prop, int action, void* arg, int64_t var) { if (action == M_PROPERTY_GET) { *(int64_t *)arg = var; return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; } int m_property_float_ro(const m_option_t *prop, int action, void *arg, float var) { if (action == M_PROPERTY_GET) { *(float *)arg = var; return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; } int m_property_double_ro(const m_option_t *prop, int action, void *arg, double var) { if (action == M_PROPERTY_GET) { *(double *)arg = var; return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; } int m_property_strdup_ro(const struct m_option* prop, int action, void* arg, const char *var) { if (action == M_PROPERTY_GET) { if (!var) return M_PROPERTY_UNAVAILABLE; *(char **)arg = talloc_strdup(NULL, var); return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; }