diff options
Diffstat (limited to 'options/parse_commandline.c')
-rw-r--r-- | options/parse_commandline.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/options/parse_commandline.c b/options/parse_commandline.c new file mode 100644 index 0000000..93120d3 --- /dev/null +++ b/options/parse_commandline.c @@ -0,0 +1,261 @@ +/* + * 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 <stdbool.h> + +#include "osdep/io.h" +#include "common/global.h" +#include "common/msg.h" +#include "common/msg_control.h" +#include "m_option.h" +#include "m_config_frontend.h" +#include "options.h" +#include "common/playlist.h" +#include "parse_commandline.h" + +#define GLOBAL 0 +#define LOCAL 1 + +struct parse_state { + struct m_config *config; + char **argv; + struct mp_log *log; // silent if NULL + + bool no_more_opts; + bool error; + + bool is_opt; + struct bstr arg; + struct bstr param; +}; + +// Returns true if more args, false if all parsed or an error occurred. +static bool split_opt(struct parse_state *p) +{ + assert(!p->error); + + if (!p->argv || !p->argv[0]) + return false; + + p->is_opt = false; + p->arg = bstr0(p->argv[0]); + p->param = bstr0(NULL); + + p->argv++; + + if (p->no_more_opts || !bstr_startswith0(p->arg, "-") || p->arg.len == 1) + return true; + + if (bstrcmp0(p->arg, "--") == 0) { + p->no_more_opts = true; + return split_opt(p); + } + + p->is_opt = true; + + bool new_opt = bstr_eatstart0(&p->arg, "--"); + if (!new_opt) + bstr_eatstart0(&p->arg, "-"); + + bool ambiguous = !bstr_split_tok(p->arg, "=", &p->arg, &p->param); + + bool need_param = m_config_option_requires_param(p->config, p->arg) > 0; + + if (ambiguous && need_param) { + if (!p->argv[0] || new_opt) { + p->error = true; + MP_FATAL(p, "Error parsing commandline option %.*s: %s\n", + BSTR_P(p->arg), m_option_strerror(M_OPT_MISSING_PARAM)); + MP_WARN(p, "Make sure you're using e.g. '--%.*s=value' instead " + "of '--%.*s value'.\n", BSTR_P(p->arg), BSTR_P(p->arg)); + return false; + } + p->param = bstr0(p->argv[0]); + p->argv++; + } + + return true; +} + +#ifdef __MINGW32__ +static void process_non_option(struct playlist *files, const char *arg) +{ + glob_t gg; + + // Glob filenames on Windows (cmd.exe doesn't do this automatically) + if (glob(arg, 0, NULL, &gg)) { + playlist_add_file(files, arg); + } else { + for (int i = 0; i < gg.gl_pathc; i++) + playlist_add_file(files, gg.gl_pathv[i]); + + globfree(&gg); + } +} +#else +static void process_non_option(struct playlist *files, const char *arg) +{ + playlist_add_file(files, arg); +} +#endif + +// returns M_OPT_... error code +int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files, + struct mpv_global *global, char **argv) +{ + int ret = M_OPT_UNKNOWN; + int mode = 0; + struct playlist_entry *local_start = NULL; + + int local_params_count = 0; + struct playlist_param *local_params = 0; + + assert(config != NULL); + + mode = GLOBAL; + + struct parse_state p = {config, argv, config->log}; + while (split_opt(&p)) { + if (p.is_opt) { + int flags = M_SETOPT_FROM_CMDLINE; + if (mode == LOCAL) + flags |= M_SETOPT_BACKUP | M_SETOPT_CHECK_ONLY; + int r = m_config_set_option_cli(config, p.arg, p.param, flags); + if (r == M_OPT_EXIT) { + ret = r; + goto err_out; + } else if (r < 0) { + MP_FATAL(config, "Setting commandline option --%.*s=%.*s failed.\n", + BSTR_P(p.arg), BSTR_P(p.param)); + goto err_out; + } + + // Handle some special arguments outside option parser. + + if (!bstrcmp0(p.arg, "{")) { + if (mode != GLOBAL) { + MP_ERR(config, "'--{' can not be nested.\n"); + goto err_out; + } + mode = LOCAL; + assert(!local_start); + local_start = playlist_get_last(files); + continue; + } + + if (!bstrcmp0(p.arg, "}")) { + if (mode != LOCAL) { + MP_ERR(config, "Too many closing '--}'.\n"); + goto err_out; + } + if (local_params_count) { + // The files added between '{' and '}' are the entries from + // the entry _after_ local_start, until the end of the list. + // If local_start is NULL, the list was empty on '{', and we + // want all files in the list. + struct playlist_entry *cur = local_start + ? playlist_entry_get_rel(local_start, 1) + : playlist_get_first(files); + if (!cur) + MP_WARN(config, "Ignored options!\n"); + while (cur) { + playlist_entry_add_params(cur, local_params, + local_params_count); + cur = playlist_entry_get_rel(cur, 1); + } + } + local_params_count = 0; + mode = GLOBAL; + m_config_restore_backups(config); + local_start = NULL; + continue; + } + + if (bstrcmp0(p.arg, "playlist") == 0) { + // append the playlist to the local args + char *param0 = bstrdup0(NULL, p.param); + struct playlist *pl = playlist_parse_file(param0, NULL, global); + if (!pl) { + MP_FATAL(config, "Error reading playlist '%.*s'\n", + BSTR_P(p.param)); + talloc_free(param0); + goto err_out; + } + playlist_transfer_entries(files, pl); + playlist_populate_playlist_path(files, param0); + talloc_free(param0); + talloc_free(pl); + continue; + } + + if (mode == LOCAL) { + MP_TARRAY_APPEND(NULL, local_params, local_params_count, + (struct playlist_param) {p.arg, p.param}); + } + } else { + // filename + void *tmp = talloc_new(NULL); + char *file0 = bstrdup0(tmp, p.arg); + process_non_option(files, file0); + talloc_free(tmp); + } + } + + if (p.error) + goto err_out; + + if (mode != GLOBAL) { + MP_ERR(config, "Missing closing --} on command line.\n"); + goto err_out; + } + + ret = 0; // success + +err_out: + talloc_free(local_params); + m_config_restore_backups(config); + return ret; +} + +/* Parse some command line options early before main parsing. + * --no-config prevents reading configuration files (otherwise done before + * command line parsing), and --really-quiet suppresses messages printed + * during normal options parsing. + */ +void m_config_preparse_command_line(m_config_t *config, struct mpv_global *global, + int *verbose, char **argv) +{ + struct parse_state p = {config, argv, mp_null_log}; + while (split_opt(&p)) { + if (p.is_opt) { + // Ignore non-pre-parse options. They will be set later. + // Option parsing errors will be handled later as well. + int flags = M_SETOPT_FROM_CMDLINE | M_SETOPT_PRE_PARSE_ONLY; + m_config_set_option_cli(config, p.arg, p.param, flags); + if (bstrcmp0(p.arg, "v") == 0) + (*verbose)++; + } + } + + for (int n = 0; n < config->num_opts; n++) + config->opts[n].warning_was_printed = false; +} |