diff options
Diffstat (limited to 'core/parser-mpcmd.c')
-rw-r--r-- | core/parser-mpcmd.c | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/core/parser-mpcmd.c b/core/parser-mpcmd.c new file mode 100644 index 0000000000..730e4c581f --- /dev/null +++ b/core/parser-mpcmd.c @@ -0,0 +1,358 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <stdbool.h> + +#include "mp_msg.h" +#include "m_option.h" +#include "m_config.h" +#include "playlist.h" +#include "playlist_parser.h" +#include "parser-mpcmd.h" +#include "osdep/macosx_finder_args.h" + +#define GLOBAL 0 +#define LOCAL 1 + +#define dvd_range(a) (a > 0 && a < 256) + + +struct parse_state { + struct m_config *config; + int argc; + char **argv; + + bool no_more_opts; + bool error; + + const struct m_option *mp_opt; // NULL <=> it's a file arg + struct bstr arg; + struct bstr param; +}; + +// Returns 0 if a valid option/file is available, <0 on error, 1 on end of args. +static int split_opt_silent(struct parse_state *p) +{ + assert(!p->error); + + if (p->argc < 1) + return 1; + + p->mp_opt = NULL; + p->arg = bstr0(p->argv[0]); + p->param = bstr0(NULL); + + p->argc--; + p->argv++; + + if (p->no_more_opts || !bstr_startswith0(p->arg, "-") || p->arg.len == 1) + return 0; + + if (bstrcmp0(p->arg, "--") == 0) { + p->no_more_opts = true; + return split_opt_silent(p); + } + + bool old_syntax = !bstr_startswith0(p->arg, "--"); + if (old_syntax) { + p->arg = bstr_cut(p->arg, 1); + } else { + p->arg = bstr_cut(p->arg, 2); + int idx = bstrchr(p->arg, '='); + if (idx > 0) { + p->param = bstr_cut(p->arg, idx + 1); + p->arg = bstr_splice(p->arg, 0, idx); + } + } + + p->mp_opt = m_config_get_option(p->config, p->arg); + if (!p->mp_opt) { + // Automagic "no-" arguments: "--no-bla" turns into "--bla=no". + if (!bstr_startswith0(p->arg, "no-")) + return -1; + + struct bstr s = bstr_cut(p->arg, 3); + p->mp_opt = m_config_get_option(p->config, s); + if (!p->mp_opt || p->mp_opt->type != &m_option_type_flag) + return -1; + // Avoid allowing "--no-no-bla". + if (bstr_startswith(bstr0(p->mp_opt->name), bstr0("no-"))) + return -1; + // Flag options never have parameters. + old_syntax = false; + if (p->param.len) + return -2; + p->arg = s; + p->param = bstr0("no"); + } + + if (bstr_endswith0(p->arg, "-clr")) + old_syntax = false; + + if (old_syntax && !(p->mp_opt->type->flags & M_OPT_TYPE_OLD_SYNTAX_NO_PARAM)) + { + if (p->argc < 1) + return -3; + p->param = bstr0(p->argv[0]); + p->argc--; + p->argv++; + } + + return 0; +} + +// Returns true if more args, false if all parsed or an error occurred. +static bool split_opt(struct parse_state *p) +{ + int r = split_opt_silent(p); + if (r >= 0) + return r == 0; + p->error = true; + if (r == -2) + mp_tmsg(MSGT_CFGPARSER, MSGL_ERR, + "A no-* option can't take parameters: --%.*s=%.*s\n", + BSTR_P(p->arg), BSTR_P(p->param)); + else if (r == -3) + mp_tmsg(MSGT_CFGPARSER, MSGL_ERR, + "Option %.*s needs a parameter.\n", BSTR_P(p->arg)); + else + mp_tmsg(MSGT_CFGPARSER, MSGL_ERR, + "Unknown option on the command line: %.*s\n", + BSTR_P(p->arg)); + return false; +} + +static bool parse_flag(bstr name, bstr f) +{ + struct m_option opt = {NULL, NULL, CONF_TYPE_FLAG, 0, 0, 1, NULL}; + int val = 0; + m_option_parse(&opt, name, f, &val); + return !!val; +} + +bool m_config_parse_mp_command_line(m_config_t *config, struct playlist *files, + int argc, char **argv) +{ + int mode = 0; + struct playlist_entry *local_start = NULL; + bool shuffle = false; + + int local_params_count = 0; + struct playlist_param *local_params = 0; + + assert(config != NULL); + assert(!config->file_local_mode); + + config->mode = M_COMMAND_LINE; + mode = GLOBAL; +#ifdef CONFIG_MACOSX_FINDER + if (macosx_finder_args(config, files, argc, argv)) + return true; +#endif + + struct parse_state p = {config, argc, argv}; + while (split_opt(&p)) { + if (p.mp_opt) { + int r; + if (mode == GLOBAL && !(p.mp_opt->flags & M_OPT_PRE_PARSE)) { + r = m_config_set_option(config, p.arg, p.param); + } else { + r = m_config_check_option(config, p.arg, p.param); + } + if (r <= M_OPT_EXIT) + goto err_out; + if (r < 0) { + char *msg = m_option_strerror(r); + if (!msg) + goto print_err; + mp_tmsg(MSGT_CFGPARSER, MSGL_FATAL, + "Error parsing commandline option %.*s: %s\n", + BSTR_P(p.arg), msg); + goto err_out; + } + + // Handle some special arguments outside option parser. + + if (!bstrcmp0(p.arg, "{")) { + if (mode != GLOBAL) { + mp_msg(MSGT_CFGPARSER, MSGL_ERR, + "'--{' can not be nested.\n"); + goto err_out; + } + mode = LOCAL; + // Needed for option checking. + m_config_enter_file_local(config); + assert(!local_start); + local_start = files->last; + continue; + } + + if (!bstrcmp0(p.arg, "}")) { + if (mode != LOCAL) { + mp_msg(MSGT_CFGPARSER, MSGL_ERR, + "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 ? local_start->next : files->first; + if (!cur) + mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Ignored options!\n"); + while (cur) { + playlist_entry_add_params(cur, local_params, + local_params_count); + cur = cur->next; + } + } + local_params_count = 0; + mode = GLOBAL; + m_config_leave_file_local(config); + local_start = NULL; + shuffle = false; + continue; + } + + if (bstrcmp0(p.arg, "shuffle") == 0) { + shuffle = parse_flag(p.arg, p.param); + 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); + talloc_free(param0); + if (!pl) + goto print_err; + playlist_transfer_entries(files, pl); + talloc_free(pl); + continue; + } + + if (bstrcmp0(p.arg, "v") == 0) { + verbose++; + continue; + } + + if (mode == LOCAL) { + MP_TARRAY_APPEND(NULL, local_params, local_params_count, + (struct playlist_param) {p.arg, p.param}); + } + } else { + // filename + bstr file = p.arg; + char *file0 = bstrdup0(NULL, p.arg); + // expand DVD filename entries like dvd://1-3 into component titles + if (bstr_startswith0(file, "dvd://")) { + int offset = 6; + char *splitpos = strstr(file0 + offset, "-"); + if (splitpos != NULL) { + int start_title = strtol(file0 + offset, NULL, 10); + int end_title; + //entries like dvd://-2 imply start at title 1 + if (start_title < 0) { + end_title = abs(start_title); + start_title = 1; + } else + end_title = strtol(splitpos + 1, NULL, 10); + + if (dvd_range(start_title) && dvd_range(end_title) + && (start_title < end_title)) { + for (int j = start_title; j <= end_title; j++) { + char entbuf[15]; + snprintf(entbuf, sizeof(entbuf), "dvd://%d", j); + playlist_add_file(files, entbuf); + } + } else + mp_tmsg(MSGT_CFGPARSER, MSGL_ERR, + "Invalid play entry %s\n", file0); + + } else // dvd:// or dvd://x entry + playlist_add_file(files, file0); + } else + playlist_add_file(files, file0); + talloc_free(file0); + + // Lock stdin if it will be used as input + if (bstrcmp0(file, "-") == 0) + m_config_set_option0(config, "consolecontrols", "no"); + } + } + + if (p.error) + goto err_out; + + if (mode != GLOBAL) { + mp_tmsg(MSGT_CFGPARSER, MSGL_ERR, + "Missing closing --} on command line.\n"); + goto err_out; + } + + if (shuffle) + playlist_shuffle(files); + + talloc_free(local_params); + assert(!config->file_local_mode); + return true; + +print_err: + mp_tmsg(MSGT_CFGPARSER, MSGL_FATAL, + "Error parsing option on the command line: %.*s\n", BSTR_P(p.arg)); +err_out: + talloc_free(local_params); + if (config->file_local_mode) + m_config_leave_file_local(config); + return false; +} + +extern int mp_msg_levels[]; + +/* Parse some command line options early before main parsing. + * --noconfig 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, int argc, char **argv) +{ + // Hack to shut up parser error messages + int msg_lvl_backup = mp_msg_levels[MSGT_CFGPARSER]; + mp_msg_levels[MSGT_CFGPARSER] = -11; + + struct parse_state p = {config, argc, argv}; + while (split_opt_silent(&p) == 0) { + if (p.mp_opt) { + // Ignore non-pre-parse options. They will be set later. + // Option parsing errors will be handled later as well. + if (p.mp_opt->flags & M_OPT_PRE_PARSE) + m_config_set_option(config, p.arg, p.param); + } + } + + mp_msg_levels[MSGT_CFGPARSER] = msg_lvl_backup; +} |