Something went wrong. Try again.
🎥 Command line media player
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260/* * 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/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){ mp_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 _WIN32static 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_append_file(files, arg); } else { for (int i = 0; i < gg.gl_pathc; i++) playlist_append_file(files, gg.gl_pathv[i]);
globfree(&gg); }}#elsestatic void process_non_option(struct playlist *files, const char *arg){ playlist_append_file(files, arg);}#endif
// returns M_OPT_... error codeint 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;
mp_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; mp_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); 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;}