void mp_parse_cfgfiles(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; if (!opts->load_config) return; mp_mk_config_dir(mpctx->global, ""); m_config_t *conf = mpctx->mconfig; char *section = NULL; bool encoding = opts->encode_opts && opts->encode_opts->file && opts->encode_opts->file[0]; // In encoding mode, we don't want to apply normal config options. // So we "divert" normal options into a separate section, and the diverted // section is never used - unless maybe it's explicitly referenced from an // encoding profile. if (encoding) section = "playback-default"; // The #if is a stupid hack to avoid errors if libavfilter is not available. #if HAVE_LIBAVFILTER && HAVE_ENCODING char *cf = mp_find_config_file(NULL, mpctx->global, "encoding-profiles.conf"); if (cf) m_config_parse_config_file(mpctx->mconfig, cf, SECT_ENCODE, 0); talloc_free(cf); #endif load_all_cfgfiles(mpctx, section, "config"); load_all_cfgfiles(mpctx, section, "mpv.conf"); if (encoding) m_config_set_profile(conf, m_config_add_profile(conf, SECT_ENCODE), 0); }
bool mp_parse_cfgfiles(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; if (!opts->load_config) return true; m_config_t *conf = mpctx->mconfig; void *tmp = talloc_new(NULL); bool r = true; char *conffile; char *section = NULL; bool encoding = opts->encode_output.file && *opts->encode_output.file; // In encoding mode, we don't want to apply normal config options. // So we "divert" normal options into a separate section, and the diverted // section is never used - unless maybe it's explicitly referenced from an // encoding profile. if (encoding) section = "playback-default"; // The #if is a stupid hack to avoid errors if libavfilter is not available. #if HAVE_LIBAVFILTER && HAVE_ENCODING conffile = mp_find_config_file(tmp, mpctx->global, "encoding-profiles.conf"); if (conffile && mp_path_exists(conffile)) m_config_parse_config_file(mpctx->mconfig, conffile, SECT_ENCODE, 0); #endif conffile = mp_find_global_config_file(tmp, mpctx->global, "mpv.conf"); if (conffile && m_config_parse_config_file(conf, conffile, section, 0) < 0) { r = false; goto done; } mp_mk_config_dir(mpctx->global, NULL); if (!(conffile = mp_find_user_config_file(tmp, mpctx->global, "config"))) MP_ERR(mpctx, "mp_find_user_config_file(\"config\") problem\n"); else if (m_config_parse_config_file(conf, conffile, section, 0) < 0) { r = false; goto done; } if (encoding) m_config_set_profile(conf, m_config_add_profile(conf, SECT_ENCODE), 0); done: talloc_free(tmp); return r; }
/** \param config The config object. * \param conffile Path to the config file. * \return 1 on sucess, -1 on error. */ int m_config_parse_config_file(m_config_t *config, const char *conffile) { #define PRINT_LINENUM mp_msg(MSGT_CFGPARSER, MSGL_V, "%s(%d): ", conffile, line_num) #define MAX_LINE_LEN 10000 #define MAX_OPT_LEN 1000 #define MAX_PARAM_LEN 1500 FILE *fp; char *line; char opt[MAX_OPT_LEN + 1]; char param[MAX_PARAM_LEN + 1]; char c; /* for the "" and '' check */ int tmp; int line_num = 0; int line_pos; /* line pos */ int opt_pos; /* opt pos */ int param_pos; /* param pos */ int ret = 1; int errors = 0; int prev_mode = config->mode; m_profile_t *profile = NULL; mp_msg(MSGT_CFGPARSER, MSGL_V, "Reading config file %s", conffile); if (recursion_depth > MAX_RECURSION_DEPTH) { mp_msg(MSGT_CFGPARSER, MSGL_ERR, ": too deep 'include'. check your configfiles\n"); ret = -1; goto out; } else config->mode = M_CONFIG_FILE; if ((line = malloc(MAX_LINE_LEN + 1)) == NULL) { mp_msg(MSGT_CFGPARSER, MSGL_FATAL, "\ncan't get memory for 'line': %s", strerror(errno)); ret = -1; goto out; } else mp_msg(MSGT_CFGPARSER, MSGL_V, "\n"); if ((fp = fopen(conffile, "r")) == NULL) { mp_msg(MSGT_CFGPARSER, MSGL_V, ": %s\n", strerror(errno)); free(line); ret = 0; goto out; } while (fgets(line, MAX_LINE_LEN, fp)) { if (errors >= 16) { mp_msg(MSGT_CFGPARSER, MSGL_FATAL, "too many errors\n"); goto out; } line_num++; line_pos = 0; /* skip whitespaces */ while (isspace(line[line_pos])) ++line_pos; /* EOL / comment */ if (line[line_pos] == '\0' || line[line_pos] == '#') continue; /* read option. */ for (opt_pos = 0; isprint(line[line_pos]) && line[line_pos] != ' ' && line[line_pos] != '#' && line[line_pos] != '='; /* NOTHING */) { opt[opt_pos++] = line[line_pos++]; if (opt_pos >= MAX_OPT_LEN) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "too long option at line %d\n", line_num); errors++; ret = -1; goto nextline; } } if (opt_pos == 0) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parse error at line %d\n", line_num); ret = -1; errors++; continue; } opt[opt_pos] = '\0'; /* Profile declaration */ if (opt_pos > 2 && opt[0] == '[' && opt[opt_pos - 1] == ']') { opt[opt_pos - 1] = '\0'; if (strcmp(opt + 1, "default")) profile = m_config_add_profile(config, opt + 1); else profile = NULL; continue; } /* skip whitespaces */ while (isspace(line[line_pos])) ++line_pos; /* check '=' */ if (line[line_pos++] != '=') { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s needs a parameter at line %d\n", opt, line_num); ret = -1; errors++; continue; } /* whitespaces... */ while (isspace(line[line_pos])) ++line_pos; /* read the parameter */ if (line[line_pos] == '"' || line[line_pos] == '\'') { c = line[line_pos]; ++line_pos; for (param_pos = 0; line[line_pos] != c; /* NOTHING */) { param[param_pos++] = line[line_pos++]; if (param_pos >= MAX_PARAM_LEN) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s has a too long parameter at line %d\n", opt, line_num); ret = -1; errors++; goto nextline; } } line_pos++; /* skip the closing " or ' */ } else { for (param_pos = 0; isprint(line[line_pos]) && !isspace(line[line_pos]) && line[line_pos] != '#'; /* NOTHING */) { param[param_pos++] = line[line_pos++]; if (param_pos >= MAX_PARAM_LEN) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "too long parameter\n"); ret = -1; errors++; goto nextline; } } } param[param_pos] = '\0'; /* did we read a parameter? */ if (param_pos == 0) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s needs a parameter at line %d\n", opt, line_num); ret = -1; errors++; continue; } /* now, check if we have some more chars on the line */ /* whitespace... */ while (isspace(line[line_pos])) ++line_pos; /* EOL / comment */ if (line[line_pos] != '\0' && line[line_pos] != '#') { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_WARN, "extra characters on line %d: %s\n", line_num, line + line_pos); ret = -1; } if (profile) { if (!strcmp(opt, "profile-desc")) m_profile_set_desc(profile, param), tmp = 1; else tmp = m_config_set_profile_option(config, profile, opt, param); } else tmp = m_config_set_option0(config, opt, param, false); if (tmp < 0) { PRINT_LINENUM; if (tmp == M_OPT_UNKNOWN) { mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Warning unknown option %s at line %d\n", opt, line_num); continue; } mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Error parsing option %s=%s at line %d\n", opt, param, line_num); ret = -1; errors++; continue; /* break */ } nextline: ; } free(line); fclose(fp); out: config->mode = prev_mode; --recursion_depth; return ret; }
/** \param config The config object. * \param conffile Path to the config file. * \return 1 on sucess, -1 on error, 0 if file not accessible. */ int m_config_parse_config_file(m_config_t *config, const char *conffile) { #define PRINT_LINENUM mp_msg(MSGT_CFGPARSER, MSGL_ERR, "%s:%d: ", conffile, line_num) #define MAX_LINE_LEN 10000 #define MAX_OPT_LEN 1000 #define MAX_PARAM_LEN 1500 FILE *fp = NULL; char *line = NULL; char opt[MAX_OPT_LEN + 1]; char param[MAX_PARAM_LEN + 1]; char c; /* for the "" and '' check */ int tmp; int line_num = 0; int line_pos; /* line pos */ int opt_pos; /* opt pos */ int param_pos; /* param pos */ int ret = 1; int errors = 0; m_profile_t *profile = NULL; mp_msg(MSGT_CFGPARSER, MSGL_V, "Reading config file %s", conffile); if (recursion_depth > MAX_RECURSION_DEPTH) { mp_msg(MSGT_CFGPARSER, MSGL_ERR, ": too deep 'include'. check your configfiles\n"); ret = -1; goto out; } if ((line = malloc(MAX_LINE_LEN + 1)) == NULL) { mp_msg(MSGT_CFGPARSER, MSGL_FATAL, "\ncan't get memory for 'line': %s", strerror(errno)); ret = -1; goto out; } else mp_msg(MSGT_CFGPARSER, MSGL_V, "\n"); if ((fp = fopen(conffile, "r")) == NULL) { mp_msg(MSGT_CFGPARSER, MSGL_V, ": %s\n", strerror(errno)); ret = 0; goto out; } while (fgets(line, MAX_LINE_LEN, fp)) { if (errors >= 16) { mp_msg(MSGT_CFGPARSER, MSGL_FATAL, "too many errors\n"); goto out; } line_num++; line_pos = 0; /* skip whitespaces */ while (isspace(line[line_pos])) ++line_pos; /* EOL / comment */ if (line[line_pos] == '\0' || line[line_pos] == '#') continue; /* read option. */ for (opt_pos = 0; isprint(line[line_pos]) && line[line_pos] != ' ' && line[line_pos] != '#' && line[line_pos] != '='; /* NOTHING */) { opt[opt_pos++] = line[line_pos++]; if (opt_pos >= MAX_OPT_LEN) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "too long option\n"); errors++; ret = -1; goto nextline; } } if (opt_pos == 0) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parse error\n"); ret = -1; errors++; continue; } opt[opt_pos] = '\0'; /* Profile declaration */ if (opt_pos > 2 && opt[0] == '[' && opt[opt_pos - 1] == ']') { opt[opt_pos - 1] = '\0'; if (strcmp(opt + 1, "default")) profile = m_config_add_profile(config, opt + 1); else profile = NULL; continue; } /* skip whitespaces */ while (isspace(line[line_pos])) ++line_pos; param_pos = 0; bool param_set = false; /* check '=' */ if (line[line_pos] == '=') { line_pos++; param_set = true; /* whitespaces... */ while (isspace(line[line_pos])) ++line_pos; /* read the parameter */ if (line[line_pos] == '"' || line[line_pos] == '\'') { c = line[line_pos]; ++line_pos; for (param_pos = 0; line[line_pos] != c; /* NOTHING */) { param[param_pos++] = line[line_pos++]; if (param_pos >= MAX_PARAM_LEN) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "option %s has a too long parameter\n", opt); ret = -1; errors++; goto nextline; } } line_pos++; /* skip the closing " or ' */ } else { for (param_pos = 0; isprint(line[line_pos]) && !isspace(line[line_pos]) && line[line_pos] != '#'; /* NOTHING */) { param[param_pos++] = line[line_pos++]; if (param_pos >= MAX_PARAM_LEN) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "too long parameter\n"); ret = -1; errors++; goto nextline; } } } while (isspace(line[line_pos])) ++line_pos; } param[param_pos] = '\0'; /* EOL / comment */ if (line[line_pos] != '\0' && line[line_pos] != '#') { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "extra characters: %s\n", line + line_pos); ret = -1; } bstr bopt = bstr0(opt); bstr bparam = bstr0(param); if (profile && bstr_equals0(bopt, "profile-desc")) { m_profile_set_desc(profile, param); goto nextline; } tmp = m_config_option_requires_param(config, bopt); if (tmp > 0 && !param_set) tmp = M_OPT_MISSING_PARAM; if (tmp < 0) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "error parsing option %s=%s: %s\n", opt, param, m_option_strerror(tmp)); continue; } if (profile) { tmp = m_config_set_profile_option(config, profile, bopt, bparam); } else { tmp = m_config_set_option_ext(config, bopt, bparam, M_SETOPT_FROM_CONFIG_FILE); } if (tmp < 0) { PRINT_LINENUM; mp_msg(MSGT_CFGPARSER, MSGL_ERR, "setting option %s='%s' failed.\n", opt, param); continue; /* break */ } nextline: ; } out: free(line); if (fp) fclose(fp); --recursion_depth; if (ret < 0) { mp_msg(MSGT_CFGPARSER, MSGL_FATAL, "Error loading config file %s.\n", conffile); } return ret; }
// Load options and profiles from 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 sucess, -1 on error, 0 if file not accessible. int m_config_parse_config_file(m_config_t *config, const char *conffile, char *initial_section, int flags) { #define PRINT_LINENUM MP_ERR(config, "%s:%d: ", conffile, line_num) #define MAX_LINE_LEN 10000 #define MAX_OPT_LEN 1000 #define MAX_PARAM_LEN 1500 FILE *fp = NULL; char *line = NULL; char opt[MAX_OPT_LEN + 1]; char param[MAX_PARAM_LEN + 1]; char c; /* for the "" and '' check */ int tmp; int line_num = 0; int line_pos; /* line pos */ int opt_pos; /* opt pos */ int param_pos; /* param pos */ int ret = 1; int errors = 0; m_profile_t *profile = m_config_add_profile(config, initial_section); flags = flags | M_SETOPT_FROM_CONFIG_FILE; MP_VERBOSE(config, "Reading config file %s\n", conffile); if (config->recursion_depth > MAX_RECURSION_DEPTH) { MP_ERR(config, "Maximum 'include' nesting depth exceeded.\n"); ret = -1; goto out; } if ((line = malloc(MAX_LINE_LEN + 1)) == NULL) { ret = -1; goto out; } else MP_VERBOSE(config, "\n"); if ((fp = fopen(conffile, "r")) == NULL) { MP_VERBOSE(config, "Can't open config file: %s\n", strerror(errno)); ret = 0; goto out; } while (fgets(line, MAX_LINE_LEN, fp)) { if (errors >= 16) { MP_FATAL(config, "too many errors\n"); goto out; } line_num++; line_pos = 0; /* skip BOM */ if (strncmp(line, "\xEF\xBB\xBF", 3) == 0) line_pos += 3; /* skip whitespaces */ while (mp_isspace(line[line_pos])) ++line_pos; /* EOL / comment */ if (line[line_pos] == '\0' || line[line_pos] == '#') continue; /* read option. */ for (opt_pos = 0; mp_isprint(line[line_pos]) && line[line_pos] != ' ' && line[line_pos] != '#' && line[line_pos] != '='; /* NOTHING */) { opt[opt_pos++] = line[line_pos++]; if (opt_pos >= MAX_OPT_LEN) { PRINT_LINENUM; MP_ERR(config, "option name too long\n"); errors++; ret = -1; goto nextline; } } if (opt_pos == 0) { PRINT_LINENUM; MP_ERR(config, "parse error\n"); ret = -1; errors++; continue; } opt[opt_pos] = '\0'; /* Profile declaration */ if (opt_pos > 2 && opt[0] == '[' && opt[opt_pos - 1] == ']') { opt[opt_pos - 1] = '\0'; profile = m_config_add_profile(config, opt + 1); continue; } /* skip whitespaces */ while (mp_isspace(line[line_pos])) ++line_pos; param_pos = 0; bool param_set = false; /* check '=' */ if (line[line_pos] == '=') { line_pos++; param_set = true; /* whitespaces... */ while (mp_isspace(line[line_pos])) ++line_pos; /* read the parameter */ if (line[line_pos] == '"' || line[line_pos] == '\'') { c = line[line_pos]; ++line_pos; for (param_pos = 0; line[line_pos] != c; /* NOTHING */) { param[param_pos++] = line[line_pos++]; if (param_pos >= MAX_PARAM_LEN) { PRINT_LINENUM; MP_ERR(config, "option %s has a too long parameter\n", opt); ret = -1; errors++; goto nextline; } } line_pos++; /* skip the closing " or ' */ goto param_done; } if (line[line_pos] == '%') { char *start = &line[line_pos + 1]; char *end = start; unsigned long len = strtoul(start, &end, 10); if (start != end && end[0] == '%') { if (len >= MAX_PARAM_LEN - 1 || strlen(end + 1) < len) { PRINT_LINENUM; MP_ERR(config, "bogus %% length\n"); ret = -1; errors++; goto nextline; } param_pos = snprintf(param, sizeof(param), "%.*s", (int)len, end + 1); line_pos += 1 + (end - start) + 1 + len; goto param_done; } } for (param_pos = 0; mp_isprint(line[line_pos]) && !mp_isspace(line[line_pos]) && line[line_pos] != '#'; /* NOTHING */) { param[param_pos++] = line[line_pos++]; if (param_pos >= MAX_PARAM_LEN) { PRINT_LINENUM; MP_ERR(config, "too long parameter\n"); ret = -1; errors++; goto nextline; } } param_done: while (mp_isspace(line[line_pos])) ++line_pos; } param[param_pos] = '\0'; /* EOL / comment */ if (line[line_pos] != '\0' && line[line_pos] != '#') { PRINT_LINENUM; MP_ERR(config, "extra characters: %s\n", line + line_pos); ret = -1; } bstr bopt = bstr0(opt); bstr bparam = bstr0(param); if (bopt.len >= 3) bstr_eatstart0(&bopt, "--"); if (profile && bstr_equals0(bopt, "profile-desc")) { m_profile_set_desc(profile, bparam); goto nextline; } bool need_param = m_config_option_requires_param(config, bopt) > 0; if (need_param && !param_set) { PRINT_LINENUM; MP_ERR(config, "error parsing option %.*s=%.*s: %s\n", BSTR_P(bopt), BSTR_P(bparam), m_option_strerror(M_OPT_MISSING_PARAM)); continue; } if (profile) { tmp = m_config_set_profile_option(config, profile, bopt, bparam); } else { tmp = m_config_set_option_ext(config, bopt, bparam, flags); } if (tmp < 0) { PRINT_LINENUM; MP_ERR(config, "setting option %.*s='%.*s' failed.\n", BSTR_P(bopt), BSTR_P(bparam)); continue; /* break */ } nextline: ; } out: free(line); if (fp) fclose(fp); config->recursion_depth -= 1; if (ret < 0) { MP_FATAL(config, "Error loading config file %s.\n", conffile); } return ret; }
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 unparseable 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 unparseable extra characters: '%.*s'\n", loc, BSTR_P(line)); goto error; } int res; if (profile) { if (bstr_equals0(option, "profile-desc")) { m_profile_set_desc(profile, value); res = 0; } else { res = m_config_set_profile_option(config, profile, option, value); } } else { res = m_config_set_option_ext(config, option, value, flags); } 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; } } talloc_free(tmp); return 1; }