summaryrefslogtreecommitdiffstats
path: root/options/m_config.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-03-12 23:07:05 +0100
committerwm4 <wm4@nowhere>2020-03-13 16:50:27 +0100
commiteb381cbd4b38dd496ee0be609f1a66c360a76448 (patch)
treefc812f68a26dc873f6f512ff6e592547bbcfd205 /options/m_config.c
parentd3ad4e23088da95697ab2ec385267c06293c4515 (diff)
downloadmpv-eb381cbd4b38dd496ee0be609f1a66c360a76448.tar.bz2
mpv-eb381cbd4b38dd496ee0be609f1a66c360a76448.tar.xz
options: split m_config.c/h
Move the "old" mostly command line parsing and option management related code to m_config_frontend.c/h. Move the the code that enables other part of the player to access options to m_config_core.c/h. "frontend" is out of lack of creativity for a better name. Unfortunately, the separation isn't quite clean yet. m_config_frontend.c still references some m_config_core.c implementation details, and m_config_new() is even left in m_config_core.c for now. There some odd functions that should be removed as well (marked as "Bad functions"). Fixing these things requires more changes and will be done separately. struct m_config is left with the current name to reduce diff noise. Also, since there are a _lot_ source files that include m_config.h, add a replacement m_config.h that "redirects" to m_config_core.h.
Diffstat (limited to 'options/m_config.c')
-rw-r--r--options/m_config.c1773
1 files changed, 0 insertions, 1773 deletions
diff --git a/options/m_config.c b/options/m_config.c
deleted file mode 100644
index e38a3b12f6..0000000000
--- a/options/m_config.c
+++ /dev/null
@@ -1,1773 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/// \file
-/// \ingroup Config
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <strings.h>
-#include <assert.h>
-#include <stdbool.h>
-#include <pthread.h>
-
-#include "libmpv/client.h"
-
-#include "mpv_talloc.h"
-
-#include "m_config.h"
-#include "options/m_option.h"
-#include "common/common.h"
-#include "common/global.h"
-#include "common/msg.h"
-#include "common/msg_control.h"
-#include "misc/dispatch.h"
-#include "misc/node.h"
-#include "osdep/atomic.h"
-
-extern const char mp_help_text[];
-
-static const union m_option_value default_value;
-
-// Profiles allow to predefine some sets of options that can then
-// be applied later on with the internal -profile option.
-#define MAX_PROFILE_DEPTH 20
-// Maximal include depth.
-#define MAX_RECURSION_DEPTH 8
-
-// Maximum possibly option name length (as it appears to the user).
-#define MAX_OPT_NAME_LEN 80
-
-// For use with m_config_cache.
-struct m_config_shadow {
- pthread_mutex_t lock;
- // Incremented on every option change.
- mp_atomic_uint64 ts;
- // -- immutable after init
- // List of m_sub_options instances.
- // Index 0 is the top-level and is always present.
- // Immutable after init.
- // Invariant: a parent is always at a lower index than any of its children.
- struct m_config_group *groups;
- int num_groups;
- // -- protected by lock
- struct m_config_data *data; // protected shadow copy of the option data
- struct config_cache **listeners;
- int num_listeners;
-};
-
-// Represents a sub-struct (OPT_SUBSTRUCT()).
-struct m_config_group {
- const struct m_sub_options *group;
- int group_count; // 1 + number of all sub groups owned by this (so
- // m_config_shadow.groups[idx..idx+group_count] is used
- // by the entire tree of sub groups included by this
- // group)
- int parent_group; // index of parent group into m_config_shadow.groups[],
- // or -1 for group 0
- int parent_ptr; // ptr offset in the parent group's data, or -1 if
- // none
- const char *prefix; // concat_name(_, prefix, opt->name) => full name
- // (the parent names are already included in this)
-};
-
-// A copy of option data. Used for the main option struct, the shadow data,
-// and copies for m_config_cache.
-struct m_config_data {
- struct m_config_shadow *shadow; // option definitions etc., main data copy
- int group_index; // start index into m_config.groups[]
- struct m_group_data *gdata; // user struct allocation (our copy of data)
- int num_gdata; // (group_index+num_gdata = end index)
-};
-
-struct config_cache {
- struct m_config_cache *public;
-
- struct m_config_data *data; // public data
- struct m_config_data *src; // global data (currently ==shadow->data)
- struct m_config_shadow *shadow; // global metadata
- uint64_t ts; // timestamp of this data copy
- bool in_list; // part of m_config_shadow->listeners[]
- int upd_group; // for "incremental" change notification
- int upd_opt;
-
-
- // --- Implicitly synchronized by setting/unsetting wakeup_cb.
- struct mp_dispatch_queue *wakeup_dispatch_queue;
- void (*wakeup_dispatch_cb)(void *ctx);
- void *wakeup_dispatch_cb_ctx;
-
- // --- Protected by shadow->lock
- void (*wakeup_cb)(void *ctx);
- void *wakeup_cb_ctx;
-};
-
-// Per m_config_data state for each m_config_group.
-struct m_group_data {
- char *udata; // pointer to group user option struct
- uint64_t ts; // timestamp of the data copy
-};
-
-struct m_profile {
- struct m_profile *next;
- char *name;
- char *desc;
- int num_opts;
- // Option/value pair array.
- char **opts;
-};
-
-// In the file local case, this contains the old global value.
-struct m_opt_backup {
- struct m_opt_backup *next;
- struct m_config_option *co;
- void *backup;
-};
-
-static struct m_config_cache *m_config_cache_alloc_internal(void *ta_parent,
- struct m_config_shadow *shadow,
- const struct m_sub_options *group);
-static void add_sub_group(struct m_config_shadow *shadow, const char *name_prefix,
- int parent_group_index, int parent_ptr,
- const struct m_sub_options *subopts);
-
-static struct m_group_data *m_config_gdata(struct m_config_data *data,
- int group_index)
-{
- if (group_index < data->group_index ||
- group_index >= data->group_index + data->num_gdata)
- return NULL;
-
- return &data->gdata[group_index - data->group_index];
-}
-
-// Like concat_name(), but returns either a, b, or buf. buf/buf_size is used as
-// target for snprintf(). (buf_size is recommended to be MAX_OPT_NAME_LEN.)
-static const char *concat_name_buf(char *buf, size_t buf_size,
- const char *a, const char *b)
-{
- assert(a);
- assert(b);
- if (!a[0])
- return b;
- if (!b[0])
- return a;
- snprintf(buf, buf_size, "%s-%s", a, b);
- return buf;
-}
-
-// Return full option name from prefix (a) and option name (b). Returns either
-// a, b, or a talloc'ed string under ta_parent.
-static const char *concat_name(void *ta_parent, const char *a, const char *b)
-{
- char buf[MAX_OPT_NAME_LEN];
- const char *r = concat_name_buf(buf, sizeof(buf), a, b);
- return r == buf ? talloc_strdup(ta_parent, r) : r;
-}
-
-struct opt_iterate_state {
- // User can read these fields.
- int group_index;
- int opt_index;
- const struct m_option *opt;
- const char *full_name; // may point to name_buf
-
- // Internal.
- int group_index_end;
- char name_buf[MAX_OPT_NAME_LEN];
- struct m_config_shadow *shadow;
-};
-
-// Start iterating all options and sub-options in the given group.
-static void opt_iterate_init(struct opt_iterate_state *iter,
- struct m_config_shadow *shadow, int group_index)
-{
- assert(group_index >= 0 && group_index < shadow->num_groups);
- iter->group_index = group_index;
- iter->group_index_end = group_index + shadow->groups[group_index].group_count;
- iter->opt_index = -1;
- iter->shadow = shadow;
-}
-
-// Get the first or next option. Returns false if end reached. If this returns
-// true, most fields in *iter are valid.
-// This does not return pseudo-option entries (like m_option_type_subconfig).
-static bool opt_iterate_next(struct opt_iterate_state *iter)
-{
- if (iter->group_index < 0)
- return false;
-
-
- while (1) {
- if (iter->group_index >= iter->group_index_end) {
- iter->group_index = -1;
- return false;
- }
-
- iter->opt_index += 1;
-
- struct m_config_group *g = &iter->shadow->groups[iter->group_index];
- const struct m_option *opts = g->group->opts;
-
- if (!opts || !opts[iter->opt_index].name) {
- iter->group_index += 1;
- iter->opt_index = -1;
- continue;
- }
-
- iter->opt = &opts[iter->opt_index];
-
- if (iter->opt->type == &m_option_type_subconfig)
- continue;
-
- iter->full_name = concat_name_buf(iter->name_buf, sizeof(iter->name_buf),
- g->prefix, iter->opt->name);
- return true;
- }
- assert(0);
-}
-
-static void list_profiles(struct m_config *config)
-{
- MP_INFO(config, "Available profiles:\n");
- for (struct m_profile *p = config->profiles; p; p = p->next)
- MP_INFO(config, "\t%s\t%s\n", p->name, p->desc ? p->desc : "");
- MP_INFO(config, "\n");
-}
-
-static int show_profile(struct m_config *config, bstr param)
-{
- struct m_profile *p;
- if (!param.len) {
- list_profiles(config);
- return M_OPT_EXIT;
- }
- if (!(p = m_config_get_profile(config, param))) {
- MP_ERR(config, "Unknown profile '%.*s'.\n", BSTR_P(param));
- return M_OPT_EXIT;
- }
- if (!config->profile_depth)
- MP_INFO(config, "Profile %s: %s\n", p->name,
- p->desc ? p->desc : "");
- config->profile_depth++;
- for (int i = 0; i < p->num_opts; i++) {
- MP_INFO(config, "%*s%s=%s\n", config->profile_depth, "",
- p->opts[2 * i], p->opts[2 * i + 1]);
-
- if (config->profile_depth < MAX_PROFILE_DEPTH
- && !strcmp(p->opts[2*i], "profile")) {
- char *e, *list = p->opts[2 * i + 1];
- while ((e = strchr(list, ','))) {
- int l = e - list;
- if (!l)
- continue;
- show_profile(config, (bstr){list, e - list});
- list = e + 1;
- }
- if (list[0] != '\0')
- show_profile(config, bstr0(list));
- }
- }
- config->profile_depth--;
- if (!config->profile_depth)
- MP_INFO(config, "\n");
- return M_OPT_EXIT;
-}
-
-// The memcpys are supposed to work around the strict aliasing violation,
-// that would result if we just dereferenced a void** (where the void** is
-// actually casted from struct some_type* ). The dummy struct type is in
-// theory needed, because void* and struct pointers could have different
-// representations, while pointers to different struct types don't.
-static void *substruct_read_ptr(const void *ptr)
-{
- struct mp_dummy_ *res;
- memcpy(&res, ptr, sizeof(res));
- return res;
-}
-static void substruct_write_ptr(void *ptr, void *val)
-{
- struct mp_dummy_ *src = val;
- memcpy(ptr, &src, sizeof(src));
-}
-
-// Initialize a field with a given value. In case this is dynamic data, it has
-// to be allocated and copied. src can alias dst.
-static void init_opt_inplace(const struct m_option *opt, void *dst,
- const void *src)
-{
- // The option will use dynamic memory allocation iff it has a free callback.
- if (opt->type->free) {
- union m_option_value temp;
- memcpy(&temp, src, opt->type->size);
- memset(dst, 0, opt->type->size);
- m_option_copy(opt, dst, &temp);
- } else if (src != dst) {
- memcpy(dst, src, opt->type->size);
- }
-}
-
-static void alloc_group(struct m_config_data *data, int group_index,
- struct m_config_data *copy)
-{
- assert(group_index == data->group_index + data->num_gdata);
- assert(group_index < data->shadow->num_groups);
- struct m_config_group *group = &data->shadow->groups[group_index];
- const struct m_sub_options *opts = group->group;
-
- MP_TARRAY_GROW(data, data->gdata, data->num_gdata);
- struct m_group_data *gdata = &data->gdata[data->num_gdata++];
-
- struct m_group_data *copy_gdata =
- copy ? m_config_gdata(copy, group_index) : NULL;
-
- *gdata = (struct m_group_data){
- .udata = talloc_zero_size(data, opts->size),
- .ts = copy_gdata ? copy_gdata->ts : 0,
- };
-
- if (opts->defaults)
- memcpy(gdata->udata, opts->defaults, opts->size);
-
- char *copy_src = copy_gdata ? copy_gdata->udata : NULL;
-
- for (int n = 0; opts->opts && opts->opts[n].name; n++) {
- const struct m_option *opt = &opts->opts[n];
-
- if (opt->offset < 0 || opt->type->size == 0)
- continue;
-
- void *dst = gdata->udata + opt->offset;
- const void *defptr = opt->defval ? opt->defval : dst;
- if (copy_src)
- defptr = copy_src + opt->offset;
-
- init_opt_inplace(opt, dst, defptr);
- }
-
- // If there's a parent, update its pointer to the new struct.
- if (group->parent_group >= data->group_index && group->parent_ptr >= 0) {
- struct m_group_data *parent_gdata =
- m_config_gdata(data, group->parent_group);
- assert(parent_gdata);
-
- substruct_write_ptr(parent_gdata->udata + group->parent_ptr, gdata->udata);
- }
-}
-
-static void free_option_data(void *p)
-{
- struct m_config_data *data = p;
-
- for (int i = 0; i < data->num_gdata; i++) {
- struct m_group_data *gdata = &data->gdata[i];
- struct m_config_group *group =
- &data->shadow->groups[data->group_index + i];
- const struct m_option *opts = group->group->opts;
-
- for (int n = 0; opts && opts[n].name; n++) {
- const struct m_option *opt = &opts[n];
-
- if (opt->offset >= 0 && opt->type->size > 0)
- m_option_free(opt, gdata->udata + opt->offset);
- }
- }
-}
-
-// Allocate data using the option description in shadow, starting at group_index
-// (index into m_config.groups[]).
-// If copy is not NULL, copy all data from there (for groups which are in both
-// m_config_data instances), in all other cases init the data with the defaults.
-static struct m_config_data *allocate_option_data(void *ta_parent,
- struct m_config_shadow *shadow,
- int group_index,
- struct m_config_data *copy)
-{
- assert(group_index >= 0 && group_index < shadow->num_groups);
- struct m_config_data *data = talloc_zero(ta_parent, struct m_config_data);
- talloc_set_destructor(data, free_option_data);
-
- data->shadow = shadow;
- data->group_index = group_index;
-
- struct m_config_group *root_group = &shadow->groups[group_index];
- assert(root_group->group_count > 0);
-
- for (int n = group_index; n < group_index + root_group->group_count; n++)
- alloc_group(data, n, copy);
-
- return data;
-}
-
-static void shadow_destroy(void *p)
-{
- struct m_config_shadow *shadow = p;
-
- // must all have been unregistered
- assert(shadow->num_listeners == 0);
-
- talloc_free(shadow->data);
- pthread_mutex_destroy(&shadow->lock);
-}
-
-static struct m_config_shadow *m_config_shadow_new(const struct m_sub_options *root)
-{
- struct m_config_shadow *shadow = talloc_zero(NULL, struct m_config_shadow);
- talloc_set_destructor(shadow, shadow_destroy);
- pthread_mutex_init(&shadow->lock, NULL);
-
- add_sub_group(shadow, NULL, -1, -1, root);
-
- if (!root->size)
- return shadow;
-
- shadow->data = allocate_option_data(shadow, shadow, 0, NULL);
-
- return shadow;
-}
-
-static void config_destroy(void *p)
-{
- struct m_config *config = p;
- config->option_change_callback = NULL;
- m_config_restore_backups(config);
-
- talloc_free(config->cache);
- talloc_free(config->shadow);
-}
-
-struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
- const struct m_sub_options *root)
-{
- struct m_config *config = talloc(talloc_ctx, struct m_config);
- talloc_set_destructor(config, config_destroy);
- *config = (struct m_config){.log = log,};
-
- config->shadow = m_config_shadow_new(root);
-
- if (root->size) {
- config->cache =
- m_config_cache_alloc_internal(config, config->shadow, root);
- config->optstruct = config->cache->opts;
- }
-
- struct opt_iterate_state it;
- opt_iterate_init(&it, config->shadow, 0);
- while (opt_iterate_next(&it)) {
- struct m_config_option co = {
- .name = talloc_strdup(config, it.full_name),
- .opt = it.opt,
- .group_index = it.group_index,
- };
-
- struct m_group_data *gdata = config->cache
- ? m_config_gdata(config->cache->internal->data, it.group_index)
- : NULL;
-
- if (gdata && co.opt->offset >= 0)
- co.data = gdata->udata + co.opt->offset;
-
- MP_TARRAY_APPEND(config, config->opts, config->num_opts, co);
- }
-
- return config;
-}
-
-static struct m_config *m_config_from_obj_desc(void *talloc_ctx,
- struct mp_log *log,
- struct mpv_global *global,
- struct m_obj_desc *desc)
-{
- struct m_sub_options *root = talloc_ptrtype(NULL, root);
- *root = (struct m_sub_options){
- .opts = desc->options,
- // (global == NULL got repurposed to mean "no alloc")
- .size = global ? desc->priv_size : 0,
- .defaults = desc->priv_defaults,
- };
-
- struct m_config *c = m_config_new(talloc_ctx, log, root);
- talloc_steal(c, root);
- c->global = global;
- return c;
-}
-
-struct m_config *m_config_from_obj_desc_noalloc(void *talloc_ctx,
- struct mp_log *log,
- struct m_obj_desc *desc)
-{
- return m_config_from_obj_desc(talloc_ctx, log, NULL, desc);
-}
-
-static const struct m_config_group *find_group(struct mpv_global *global,
- const struct m_option *cfg)
-{
- struct m_config_shadow *shadow = global->config;
-
- for (int n = 0; n < shadow->num_groups; n++) {
- if (shadow->groups[n].group->opts == cfg)
- return &shadow->groups[n];
- }
-
- return NULL;
-}
-
-void *m_config_group_from_desc(void *ta_parent, struct mp_log *log,
- struct mpv_global *global, struct m_obj_desc *desc, const char *name)
-{
- const struct m_config_group *group = find_group(global, desc->options);
- if (group) {
- return mp_get_config_group(ta_parent, global, group->group);
- } else {
- void *d = talloc_zero_size(ta_parent, desc->priv_size);
- if (desc->priv_defaults)
- memcpy(d, desc->priv_defaults, desc->priv_size);
- return d;
- }
-}
-
-static int m_config_set_obj_params(struct m_config *config, struct mp_log *log,
- struct mpv_global *global,
- struct m_obj_desc *desc, char **args)
-{
- for (int n = 0; args && args[n * 2 + 0]; n++) {
- bstr opt = bstr0(args[n * 2 + 0]);
- bstr val = bstr0(args[n * 2 + 1]);
- if (m_config_set_option_cli(config, opt, val, 0) < 0)
- return -1;
- }
-
- return 0;
-}
-
-struct m_config *m_config_from_obj_desc_and_args(void *ta_parent,
- struct mp_log *log, struct mpv_global *global, struct m_obj_desc *desc,
- const char *name, struct m_obj_settings *defaults, char **args)
-{
- struct m_config *config = m_config_from_obj_desc(ta_parent, log, global, desc);
-
- for (int n = 0; defaults && defaults[n].name; n++) {
- struct m_obj_settings *entry = &defaults[n];
- if (name && strcmp(entry->name, name) == 0) {
- if (m_config_set_obj_params(config, log, global, desc, entry->attribs) < 0)
- goto error;
- }
- }
-
- if (m_config_set_obj_params(config, log, global, desc, args) < 0)
- goto error;
-
- return config;
-error:
- talloc_free(config);
- return NULL;
-}
-
-static void ensure_backup(struct m_config *config, struct m_config_option *co)
-{
- if (!co->data)
- return;
- for (struct m_opt_backup *cur = config->backup_opts; cur; cur = cur->next) {
- if (cur->co->data == co->data) // comparing data ptr catches aliases
- return;
- }
- struct m_opt_backup *bc = talloc_ptrtype(NULL, bc);
- *bc = (struct m_opt_backup) {
- .co = co,
- .backup = talloc_zero_size(bc, co->opt->type->size),
- };
- m_option_copy(co->opt, bc->backup, co->data);
- bc->next = config->backup_opts;
- config->backup_opts = bc;
- co->is_set_locally = true;
-}
-
-void m_config_restore_backups(struct m_config *config)
-{
- while (config->backup_opts) {
- struct m_opt_backup *bc = config->backup_opts;
- config->backup_opts = bc->next;
-
- m_config_set_option_raw(config, bc->co, bc->backup, 0);
-
- m_option_free(bc->co->opt, bc->backup);
- bc->co->is_set_locally = false;
- talloc_free(bc);
- }
-}
-
-void m_config_backup_opt(struct m_config *config, const char *opt)
-{
- struct m_config_option *co = m_config_get_co(config, bstr0(opt));
- if (co) {
- ensure_backup(config, co);
- } else {
- MP_ERR(config, "Option %s not found.\n", opt);
- }
-}
-
-void m_config_backup_all_opts(struct m_config *config)
-{
- for (int n = 0; n < config->num_opts; n++)
- ensure_backup(config, &config->opts[n]);
-}
-
-static void init_obj_settings_list(struct m_config_shadow *shadow,
- int parent_group_index,
- const struct m_obj_list *list)
-{
- struct m_obj_desc desc;
- for (int n = 0; ; n++) {
- if (!list->get_desc(&desc, n))
- break;
- if (desc.global_opts) {
- add_sub_group(shadow, NULL, parent_group_index, -1,
- desc.global_opts);
- }
- if (list->use_global_options && desc.options) {
- struct m_sub_options *conf = talloc_ptrtype(shadow, conf);
- *conf = (struct m_sub_options){
- .prefix = desc.options_prefix,
- .opts = desc.options,
- .defaults = desc.priv_defaults,
- .size = desc.priv_size,
- };
- add_sub_group(shadow, NULL, parent_group_index, -1, conf);
- }
- }
-}
-
-static void add_sub_group(struct m_config_shadow *shadow, const char *name_prefix,
- int parent_group_index, int parent_ptr,
- const struct m_sub_options *subopts)
-{
- // Can't be used multiple times.
- for (int n = 0; n < shadow->num_groups; n++)
- assert(shadow->groups[n].group != subopts);
-
- if (!name_prefix)
- name_prefix = "";
- if (subopts->prefix && subopts->prefix[0]) {
- assert(!name_prefix[0]);
- name_prefix = subopts->prefix;
- }
-
- // You can only use UPDATE_ flags here.
- assert(!(subopts->change_flags & ~(unsigned)UPDATE_OPTS_MASK));
-
- assert(parent_group_index >= -1 && parent_group_index < shadow->num_groups);
-
- int group_index = shadow->num_groups++;
- MP_TARRAY_GROW(shadow, shadow->groups, group_index);
- shadow->groups[group_index] = (struct m_config_group){
- .group = subopts,
- .parent_group = parent_group_index,
- .parent_ptr = parent_ptr,
- .prefix = name_prefix,
- };
-
- for (int i = 0; subopts->opts && subopts->opts[i].name; i++) {
- const struct m_option *opt = &subopts->opts[i];
-
- if (opt->type == &m_option_type_subconfig) {
- const struct m_sub_options *new_subopts = opt->priv;
-
- // Providing default structs in-place is not allowed.
- if (opt->offset >= 0 && subopts->defaults) {
- void *ptr = (char *)subopts->defaults + opt->offset;
- assert(!substruct_read_ptr(ptr));
- }
-
- const char *prefix = concat_name(shadow, name_prefix, opt->name);
- add_sub_group(shadow, prefix, group_index, opt->offset, new_subopts);
-
- } else if (opt->type == &m_option_type_obj_settings_list) {
- const struct m_obj_list *objlist = opt->priv;
- init_obj_settings_list(shadow, group_index, objlist);
- }
- }
-
- if (subopts->get_sub_options) {
- for (int i = 0; ; i++) {
- const struct m_sub_options *sub = NULL;
- if (!subopts->get_sub_options(i, &sub))
- break;
- if (sub)
- add_sub_group(shadow, NULL, group_index, -1, sub);
- }
- }
-
- shadow->groups[group_index].group_count = shadow->num_groups - group_index;
-}
-
-static uint64_t get_option_change_mask(struct m_config_shadow *shadow,
- int group_index, int group_root,
- const struct m_option *opt)
-{
- uint64_t changed = opt->flags & UPDATE_OPTS_MASK;
- while (group_index != group_root) {
- struct m_config_group *g = &shadow->groups[group_index];
- changed |= g->group->change_flags;
- group_index = g->parent_group;
- }
- return changed;
-}
-
-struct m_config_option *m_config_get_co_raw(const struct m_config *config,
- struct bstr name)
-{
- if (!name.len)
- return NULL;
-
- for (int n = 0; n < config->num_opts; n++) {
- struct m_config_option *co = &config->opts[n];
- struct bstr coname = bstr0(co->name);
- if (bstrcmp(coname, name) == 0)
- return co;
- }
-
- return NULL;
-}
-
-// Like m_config_get_co_raw(), but resolve aliases.
-static struct m_config_option *m_config_get_co_any(const struct m_config *config,
- struct bstr name)
-{
- struct m_config_option *co = m_config_get_co_raw(config, name);
- if (!co)
- return NULL;
-
- const char *prefix = config->is_toplevel ? "--" : "";
- if (co->opt->type == &m_option_type_alias) {
- const char *alias = (const char *)co->opt->priv;
- if (co->opt->deprecation_message && !co->warning_was_printed) {
- if (co->opt->deprecation_message[0]) {
- MP_WARN(config, "Warning: option %s%s was replaced with "
- "%s%s: %s\n", prefix, co->name, prefix, alias,
- co->opt->deprecation_message);
- } else {
- MP_WARN(config, "Warning: option %s%s was replaced with "
- "%s%s and might be removed in the future.\n",
- prefix, co->name, prefix, alias);
- }
- co->warning_was_printed = true;
- }
- return m_config_get_co_any(config, bstr0(alias));
- } else if (co->opt->type == &m_option_type_removed) {
- if (!co->warning_was_printed) {
- char *msg = co->opt->priv;
- if (msg) {
- MP_FATAL(config, "Option %s%s was removed: %s\n",
- prefix, co->name, msg);
- } else {
- MP_FATAL(config, "Option %s%s was removed.\n",
- prefix, co->name);
- }
- co->warning_was_printed = true;
- }
- return NULL;
- } else if (co->opt->deprecation_message) {
- if (!co->warning_was_printed) {
- MP_WARN(config, "Warning: option %s%s is deprecated "
- "and might be removed in the future (%s).\n",
- prefix, co->name, co->opt->deprecation_message);
- co->warning_was_printed = true;
- }
- }
- return co;
-}
-
-struct m_config_option *m_config_get_co(const struct m_config *config,
- struct bstr name)
-{
- struct m_config_option *co = m_config_get_co_any(config, name);
- // CLI aliases should not be real options, and are explicitly handled by
- // m_config_set_option_cli(). So pretend it does not exist.
- if (co && co->opt->type == &m_option_type_cli_alias)
- co = NULL;
- return co;
-}
-
-int m_config_get_co_count(struct m_config *config)
-{
- return config->num_opts;
-}
-
-struct m_config_option *m_config_get_co_index(struct m_config *config, int index)
-{
- return &config->opts[index];
-}
-
-const void *m_config_get_co_default(const struct m_config *config,
- struct m_config_option *co)
-{
- if (co->opt->defval)
- return co->opt->defval;
-
- const struct m_sub_options *subopt =
- config->shadow->groups[co->group_index].group;
-
- if (co->opt->offset >= 0 && subopt->defaults)
- return (char *)subopt->defaults + co->opt->offset;
-
- return NULL;
-}
-
-const char *m_config_get_positional_option(const struct m_config *config, int p)
-{
- int pos = 0;
- for (int n = 0; n < config->num_opts; n++) {
- struct m_config_option *co = &config->opts[n];
- if (!co->opt->deprecation_message) {
- if (pos == p)
- return co->name;
- pos++;
- }
- }
- return NULL;
-}
-
-// return: <0: M_OPT_ error, 0: skip, 1: check, 2: set
-static int handle_set_opt_flags(struct m_config *config,
- struct m_config_option *co, int flags)
-{
- int optflags = co->opt->flags;
- bool set = !(flags & M_SETOPT_CHECK_ONLY);
-
- if ((flags & M_SETOPT_PRE_PARSE_ONLY) && !(optflags & M_OPT_PRE_PARSE))
- return 0;
-
- if ((flags & M_SETOPT_PRESERVE_CMDLINE) && co->is_set_from_cmdline)
- set = false;
-
- if ((flags & M_SETOPT_NO_OVERWRITE) &&
- (co->is_set_from_cmdline || co->is_set_from_config))
- set = false;
-
- if ((flags & M_SETOPT_NO_PRE_PARSE) && (optflags & M_OPT_PRE_PARSE))
- return M_OPT_INVALID;
-
- // Check if this option isn't forbidden in the current mode
- if ((flags & M_SETOPT_FROM_CONFIG_FILE) && (optflags & M_OPT_NOCFG)) {
- MP_ERR(config, "The %s option can't be used in a config file.\n",
- co->name);
- return M_OPT_INVALID;
- }
- if ((flags & M_SETOPT_BACKUP) && set)
- ensure_backup(config, co);
-
- return set ? 2 : 1;
-}
-
-void m_config_mark_co_flags(struct m_config_option *co, int flags)
-{
- if (flags & M_SETOPT_FROM_CMDLINE)
- co->is_set_from_cmdline = true;
-
- if (flags & M_SETOPT_FROM_CONFIG_FILE)
- co->is_set_from_config = true;
-}
-
-// Special options that don't really fit into the option handling model. They
-// usually store no data, but trigger actions. Caller is assumed to have called
-// handle_set_opt_flags() to make sure the option can be set.
-// Returns M_OPT_UNKNOWN if the option is not a special option.
-static int m_config_handle_special_options(struct m_config *config,
- struct m_config_option *co,
- void *data, int flags)
-{
- if (config->use_profiles && strcmp(co->name, "profile") == 0) {
- char **list = *(char ***)data;
-
- if (list && list[0] && !list[1] && strcmp(list[0], "help") == 0) {
- if (!config->profiles) {
- MP_INFO(config, "No profiles have been defined.\n");
- return M_OPT_EXIT;
- }
- list_profiles(config);
- return M_OPT_EXIT;
- }
-
- for (int n = 0; list && list[n]; n++) {
- int r = m_config_set_profile(config, list[n], flags);
- if (r < 0)
- return r;
- }
- return 0;
- }
-
- if (config->includefunc && strcmp(co->name, "include") == 0) {
- char *param = *(char **)data;
- if (!param || !param[0])
- return M_OPT_MISSING_PARAM;
- if (config->recursion_depth >= MAX_RECURSION_DEPTH) {
- MP_ERR(config, "Maximum 'include' nesting depth exceeded.\n");
- return M_OPT_INVALID;
- }
- config->recursion_depth += 1;
- config->includefunc(config->includefunc_ctx, param, flags);
- config->recursion_depth -= 1;
- if (config->recursion_depth == 0 && config->profile_depth == 0)
- m_config_finish_default_profile(config, flags);
- return 1;
- }
-
- if (config->use_profiles && strcmp(co->name, "show-profile") == 0)
- return show_profile(config, bstr0(*(char **)data));
-
- if (config->is_toplevel && (strcmp(co->name, "h") == 0 ||
- strcmp(co->name, "help") == 0))
- {
- char *h = *(char **)data;
- mp_info(config->log, "%s", mp_help_text);
- if (h && h[0])
- m_config_print_option_list(config, h);
- return M_OPT_EXIT;
- }
-
- if (strcmp(co->name, "list-options") == 0) {
- m_config_print_option_list(config, "*");
- return M_OPT_EXIT;
- }
-
- return M_OPT_UNKNOWN;
-}
-
-// This notification happens when anyone other than m_config->cache (i.e. not
-// through m_config_set_option_raw() or related) changes any options.
-static void async_change_cb(void *p)
-{
- struct m_config *config = p;
-
- void *ptr;
- while (m_config_cache_get_next_changed(config->cache, &ptr)) {
- // Regrettable linear search, might degenerate to quadratic.
-