mp_msg: print messages to stdout, statusline to stderr
[mplayer.git] / parser-mpcmd.c
blob7d666890f11d5c54cbf25916c4087e777b0ee937
1 /*
2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "config.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <stdbool.h>
28 #include "mp_msg.h"
29 #include "m_option.h"
30 #include "m_config.h"
31 #include "playtree.h"
32 #include "parser-mpcmd.h"
33 #include "osdep/macosx_finder_args.h"
35 #define GLOBAL 0
36 #define LOCAL 1
38 #define dvd_range(a) (a > 0 && a < 256)
41 static inline void add_entry(play_tree_t **last_parentp,
42 play_tree_t **last_entryp, play_tree_t *entry)
44 if (*last_entryp == NULL)
45 play_tree_set_child(*last_parentp, entry);
46 else
47 play_tree_append_entry(*last_entryp, entry);
48 *last_entryp = entry;
51 static bool split_opt(struct bstr *opt, struct bstr *param, bool *old_syntax)
53 if (!bstr_startswith0(*opt, "-") || opt->len == 1)
54 return false;
55 if (bstr_startswith0(*opt, "--")) {
56 *old_syntax = false;
57 *opt = bstr_cut(*opt, 2);
58 *param = bstr(NULL);
59 int idx = bstrchr(*opt, '=');
60 if (idx > 0) {
61 *param = bstr_cut(*opt, idx + 1);
62 *opt = bstr_splice(*opt, 0, idx);
64 } else {
65 *old_syntax = true;
66 *opt = bstr_cut(*opt, 1);
68 return true;
71 static int map_to_option(struct m_config *config, bool old_syntax,
72 const struct m_option **mp_opt,
73 struct bstr *optname, struct bstr *param)
75 if (!mp_opt)
76 mp_opt = &(const struct m_option *){0};
77 *mp_opt = m_config_get_option(config, *optname);
78 if (*mp_opt)
79 return 0;
80 if (!bstr_startswith0(*optname, "no-"))
81 return -1;
82 struct bstr s = bstr_cut(*optname, 3);
83 *mp_opt = m_config_get_option(config, s);
84 if (!*mp_opt || (*mp_opt)->type != &m_option_type_flag)
85 return -1;
86 if (param->len)
87 return -2;
88 if (old_syntax)
89 return -3;
90 *optname = s;
91 *param = bstr("no");
92 return 0;
95 // Parse command line to set up config and playtree
96 play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
97 char **argv)
99 int mode = 0;
100 bool no_more_opts = false;
101 bool opt_exit = false; // exit immediately after parsing (help options)
102 play_tree_t *last_parent, *last_entry = NULL, *root;
103 struct bstr orig_opt;
105 assert(config != NULL);
106 assert(argv != NULL);
107 assert(argc >= 1);
109 config->mode = M_COMMAND_LINE;
110 mode = GLOBAL;
111 #ifdef CONFIG_MACOSX_FINDER
112 root = macosx_finder_args(config, argc, argv);
113 if (root)
114 return root;
115 #endif
117 last_parent = root = play_tree_new();
119 for (int i = 1; i < argc; i++) {
120 //next:
121 struct bstr opt = bstr(argv[i]);
122 orig_opt = opt;
123 /* check for -- (no more options id.) except --help! */
124 if (!bstrcmp0(opt, "--")) {
125 no_more_opts = true;
126 continue;
128 if (!bstrcmp0(opt, "{")) {
129 play_tree_t *entry = play_tree_new();
130 mode = LOCAL;
131 if (last_parent->flags & PLAY_TREE_RND)
132 entry->flags |= PLAY_TREE_RND;
133 if (last_entry == NULL)
134 play_tree_set_child(last_parent, entry);
135 else {
136 play_tree_append_entry(last_entry, entry);
137 last_entry = NULL;
139 last_parent = entry;
140 continue;
143 if (!bstrcmp0(opt, "}")) {
144 if (!last_parent || !last_parent->parent) {
145 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "too much }-\n");
146 goto err_out;
148 last_entry = last_parent;
149 last_parent = last_entry->parent;
150 continue;
153 struct bstr param = bstr(i+1 < argc ? argv[i+1] : NULL);
154 bool old_syntax;
155 if (!no_more_opts && split_opt(&opt, &param, &old_syntax)) {
156 // Handle some special arguments outside option parser.
157 // --loop when it applies to a group of files (per-file is option)
158 if (bstrcasecmp0(opt, "loop") == 0 &&
159 (!last_entry || last_entry->child)) {
160 struct bstr rest;
161 int l = bstrtoll(param, &rest, 0);
162 if (!param.len || rest.len) {
163 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
164 "The loop option must be an integer: \"%.*s\"\n",
165 BSTR_P(param));
166 goto print_err;
167 } else {
168 play_tree_t *pt = last_entry ? last_entry : last_parent;
169 l = l <= 0 ? -1 : l;
170 pt->loop = l;
171 i += old_syntax;
173 } else if (bstrcasecmp0(opt, "shuffle") == 0) {
174 if (last_entry && last_entry->child)
175 last_entry->flags |= PLAY_TREE_RND;
176 else
177 last_parent->flags |= PLAY_TREE_RND;
178 } else if (bstrcasecmp0(opt, "noshuffle") == 0 ||
179 bstrcasecmp0(opt, "no-shuffle") == 0) {
180 if (last_entry && last_entry->child)
181 last_entry->flags &= ~PLAY_TREE_RND;
182 else
183 last_parent->flags &= ~PLAY_TREE_RND;
184 } else if (bstrcasecmp0(opt, "playlist") == 0) {
185 if (param.len <= 0)
186 goto print_err;
187 struct play_tree *entry = parse_playlist_file(config->optstruct,
188 param);
189 if (!entry)
190 goto print_err;
191 add_entry(&last_parent, &last_entry, entry);
192 if ((last_parent->flags & PLAY_TREE_RND) && entry->child)
193 entry->flags |= PLAY_TREE_RND;
194 mode = LOCAL;
195 i += old_syntax;
196 } else {
197 // "normal" options
198 const struct m_option *mp_opt;
199 int ok = map_to_option(config, old_syntax, &mp_opt, &opt,
200 &param);
201 if (ok < 0) {
202 if (ok == -3)
203 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
204 "Option --%.*s can't be used with single-dash "
205 "syntax\n", BSTR_P(opt));
206 else if (ok == -2)
207 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
208 "A --no-* option can't take parameters: "
209 "--%.*s=%.*s\n", BSTR_P(opt), BSTR_P(param));
210 else
211 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
212 "Unknown option on the command line: --%.*s\n",
213 BSTR_P(opt));
214 goto print_err;
216 int r;
217 if (mode == GLOBAL || (mp_opt->flags & M_OPT_GLOBAL)) {
218 r = m_config_set_option(config, opt, param, old_syntax);
219 } else {
220 r = m_config_check_option(config, opt, param, old_syntax);
221 if (r >= 0) {
222 play_tree_t *pt = last_entry ? last_entry : last_parent;
223 if (r == 0)
224 param = bstr(NULL); // for old_syntax case
225 play_tree_set_param(pt, opt, param);
228 if (r <= M_OPT_EXIT) {
229 opt_exit = true;
230 r = M_OPT_EXIT - r;
231 } else if (r < 0) {
232 char *msg = m_option_strerror(r);
233 if (!msg)
234 goto print_err;
235 mp_tmsg(MSGT_CFGPARSER, MSGL_FATAL,
236 "Error parsing commandline option \"%.*s\": %s\n",
237 BSTR_P(orig_opt), msg);
238 goto err_out;
240 if (old_syntax)
241 i += r;
243 } else { /* filename */
244 int is_dvdnav = strstr(argv[i], "dvdnav://") != NULL;
245 play_tree_t *entry = play_tree_new();
246 mp_msg(MSGT_CFGPARSER, MSGL_DBG2, "Adding file %s\n", argv[i]);
247 // expand DVD filename entries like dvd://1-3 into component titles
248 if (strstr(argv[i], "dvd://") != NULL || is_dvdnav) {
249 int offset = is_dvdnav ? 9 : 6;
250 char *splitpos = strstr(argv[i] + offset, "-");
251 if (splitpos != NULL) {
252 int start_title = strtol(argv[i] + offset, NULL, 10);
253 int end_title;
254 //entries like dvd://-2 imply start at title 1
255 if (start_title < 0) {
256 end_title = abs(start_title);
257 start_title = 1;
258 } else
259 end_title = strtol(splitpos + 1, NULL, 10);
261 if (dvd_range(start_title) && dvd_range(end_title)
262 && (start_title < end_title)) {
263 for (int j = start_title; j <= end_title; j++) {
264 if (j != start_title)
265 entry = play_tree_new();
266 char entbuf[15];
267 snprintf(entbuf, sizeof(entbuf),
268 is_dvdnav ? "dvdnav://%d" : "dvd://%d", j);
269 play_tree_add_file(entry, entbuf);
270 add_entry(&last_parent, &last_entry, entry);
271 last_entry = entry;
273 } else
274 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
275 "Invalid play entry %s\n", argv[i]);
277 } else // dvd:// or dvd://x entry
278 play_tree_add_file(entry, argv[i]);
279 } else
280 play_tree_add_file(entry, argv[i]);
282 // Lock stdin if it will be used as input
283 if (strcasecmp(argv[i], "-") == 0)
284 m_config_set_option0(config, "consolecontrols", "no", false);
285 add_entry(&last_parent, &last_entry, entry);
286 mode = LOCAL; // We start entry specific options
290 if (opt_exit)
291 goto err_out;
292 if (last_parent != root)
293 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Missing }- ?\n");
294 return root;
296 print_err:
297 mp_tmsg(MSGT_CFGPARSER, MSGL_FATAL,
298 "Error parsing option on the command line: %.*s\n",
299 BSTR_P(orig_opt));
300 err_out:
301 play_tree_free(root, 1);
302 return NULL;
305 extern int mp_msg_levels[];
307 /* Parse some command line options early before main parsing.
308 * --noconfig prevents reading configuration files (otherwise done before
309 * command line parsing), and --really-quiet suppresses messages printed
310 * during normal options parsing.
312 int m_config_preparse_command_line(m_config_t *config, int argc, char **argv,
313 int *verbose)
315 int ret = 0;
317 // Hack to shut up parser error messages
318 int msg_lvl_backup = mp_msg_levels[MSGT_CFGPARSER];
319 mp_msg_levels[MSGT_CFGPARSER] = -11;
321 config->mode = M_COMMAND_LINE_PRE_PARSE;
323 for (int i = 1 ; i < argc ; i++) {
324 struct bstr opt = bstr(argv[i]);
325 // No more options after --
326 if (!bstrcmp0(opt, "--"))
327 break;
328 struct bstr param = bstr(i+1 < argc ? argv[i+1] : NULL);
329 bool old_syntax;
330 if (!split_opt(&opt, &param, &old_syntax))
331 continue; // Ignore non-option arguments
332 // Ignore invalid options
333 if (map_to_option(config, old_syntax, NULL, &opt, &param) < 0)
334 continue;
335 // "-v" is handled here
336 if (!bstrcmp0(opt, "v")) {
337 (*verbose)++;
338 continue;
340 // Set, non-pre-parse options will be ignored
341 int r = m_config_set_option(config, opt, param, old_syntax);
342 if (r < 0)
343 ret = r;
344 else if (old_syntax)
345 i += r;
348 mp_msg_levels[MSGT_CFGPARSER] = msg_lvl_backup;
350 return ret;