diff options
Diffstat (limited to 'options/parse_configfile.c')
-rw-r--r-- | options/parse_configfile.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/options/parse_configfile.c b/options/parse_configfile.c new file mode 100644 index 0000000..edd6be9 --- /dev/null +++ b/options/parse_configfile.c @@ -0,0 +1,178 @@ +/* + * 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/>. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "osdep/io.h" + +#include "parse_configfile.h" +#include "common/common.h" +#include "common/msg.h" +#include "misc/ctype.h" +#include "m_option.h" +#include "m_config.h" +#include "stream/stream.h" + +// Skip whitespace and comments (assuming there are no line breaks) +static bool skip_ws(bstr *s) +{ + *s = bstr_lstrip(*s); + if (bstr_startswith0(*s, "#")) + s->len = 0; + return s->len; +} + +int m_config_parse(m_config_t *config, const char *location, bstr data, + char *initial_section, int flags) +{ + m_profile_t *profile = m_config_add_profile(config, initial_section); + void *tmp = talloc_new(NULL); + int line_no = 0; + int errors = 0; + + bstr_eatstart0(&data, "\xEF\xBB\xBF"); // skip BOM + + while (data.len) { + talloc_free_children(tmp); + bool ok = false; + + line_no++; + char loc[512]; + snprintf(loc, sizeof(loc), "%s:%d:", location, line_no); + + bstr line = bstr_strip_linebreaks(bstr_getline(data, &data)); + if (!skip_ws(&line)) + continue; + + // Profile declaration + if (bstr_eatstart0(&line, "[")) { + bstr profilename; + if (!bstr_split_tok(line, "]", &profilename, &line)) { + MP_ERR(config, "%s missing closing ]\n", loc); + goto error; + } + if (skip_ws(&line)) { + MP_ERR(config, "%s unparsable extra characters: '%.*s'\n", + loc, BSTR_P(line)); + goto error; + } + profile = m_config_add_profile(config, bstrto0(tmp, profilename)); + continue; + } + + bstr_eatstart0(&line, "--"); + + bstr option = line; + while (line.len && (mp_isalnum(line.start[0]) || line.start[0] == '_' || + line.start[0] == '-')) + line = bstr_cut(line, 1); + option.len = option.len - line.len; + skip_ws(&line); + + bstr value = {0}; + if (bstr_eatstart0(&line, "=")) { + skip_ws(&line); + if (line.len && (line.start[0] == '"' || line.start[0] == '\'')) { + // Simple quoting, like "value" + char term[2] = {line.start[0], 0}; + line = bstr_cut(line, 1); + if (!bstr_split_tok(line, term, &value, &line)) { + MP_ERR(config, "%s unterminated quote\n", loc); + goto error; + } + } else if (bstr_eatstart0(&line, "%")) { + // Quoting with length, like %5%value + bstr rest; + long long len = bstrtoll(line, &rest, 10); + if (rest.len == line.len || !bstr_eatstart0(&rest, "%") || + len > rest.len) + { + MP_ERR(config, "%s fixed-length quoting expected - put " + "\"quotes\" around the option value if you did not " + "intend to use this, but your option value starts " + "with '%%'\n", loc); + goto error; + } + value = bstr_splice(rest, 0, len); + line = bstr_cut(rest, len); + } else { + // No quoting; take everything until the comment or end of line + int end = bstrchr(line, '#'); + value = bstr_strip(end < 0 ? line : bstr_splice(line, 0, end)); + line.len = 0; + } + } + if (skip_ws(&line)) { + MP_ERR(config, "%s unparsable extra characters: '%.*s'\n", + loc, BSTR_P(line)); + goto error; + } + + int res = m_config_set_profile_option(config, profile, option, value); + if (res < 0) { + MP_ERR(config, "%s setting option %.*s='%.*s' failed.\n", + loc, BSTR_P(option), BSTR_P(value)); + goto error; + } + + ok = true; + error: + if (!ok) + errors++; + if (errors > 16) { + MP_ERR(config, "%s: too many errors, stopping.\n", location); + break; + } + } + + if (config->recursion_depth == 0) + m_config_finish_default_profile(config, flags); + + talloc_free(tmp); + return 1; +} + +// Load options and profiles from a config file. +// conffile: path to the config file +// initial_section: default section where to add normal options +// flags: M_SETOPT_* bits +// returns: 1 on success, -1 on error, 0 if file not accessible. +int m_config_parse_config_file(m_config_t *config, struct mpv_global *global, + const char *conffile, char *initial_section, + int flags) +{ + flags = flags | M_SETOPT_FROM_CONFIG_FILE; + + MP_VERBOSE(config, "Reading config file %s\n", conffile); + + struct stream *s = stream_create(conffile, STREAM_READ | STREAM_ORIGIN_DIRECT, + NULL, global); + if (!s) + return 0; + bstr data = stream_read_complete(s, s, 1000000000); + if (!data.start) + return 0; + + int r = m_config_parse(config, conffile, data, initial_section, flags); + talloc_free(data.start); + free_stream(s); + return r; +} |