options: support "no-foo" syntax for flag suboptions
[mplayer.git] / m_option.c
blob18f94dc7a9a764a3677c550b4539a6e0e5da9a0c
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 /// \file
20 /// \ingroup Options
22 #include "config.h"
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <inttypes.h>
30 #include <unistd.h>
31 #include <assert.h>
33 #include "talloc.h"
34 #include "m_option.h"
35 #include "mp_msg.h"
36 #include "stream/url.h"
37 #include "libavutil/avstring.h"
39 char *m_option_strerror(int code)
41 switch (code) {
42 case M_OPT_UNKNOWN:
43 return mp_gtext("Unrecognized option name");
44 case M_OPT_MISSING_PARAM:
45 return mp_gtext("Required parameter for option missing");
46 case M_OPT_INVALID:
47 return mp_gtext("Option parameter could not be parsed");
48 case M_OPT_OUT_OF_RANGE:
49 return mp_gtext("Parameter is outside values allowed for option");
50 case M_OPT_PARSER_ERR:
51 return mp_gtext("Parser error");
52 default:
53 return NULL;
57 static const struct m_option *m_option_list_findb(const struct m_option *list,
58 struct bstr name)
60 for (int i = 0; list[i].name; i++) {
61 struct bstr lname = bstr(list[i].name);
62 if ((list[i].type->flags & M_OPT_TYPE_ALLOW_WILDCARD)
63 && bstr_endswith0(lname, "*")) {
64 lname.len--;
65 if (bstrcasecmp(bstr_splice(name, 0, lname.len), lname) == 0)
66 return &list[i];
67 } else if (bstrcasecmp(lname, name) == 0)
68 return &list[i];
70 return NULL;
73 const m_option_t *m_option_list_find(const m_option_t *list, const char *name)
75 return m_option_list_findb(list, bstr(name));
78 // Default function that just does a memcpy
80 static void copy_opt(const m_option_t *opt, void *dst, const void *src,
81 void *talloc_ctx)
83 if (dst && src)
84 memcpy(dst, src, opt->type->size);
87 // Flag
89 #define VAL(x) (*(int *)(x))
91 static int parse_flag(const m_option_t *opt, struct bstr name,
92 struct bstr param, bool ambiguous_param, void *dst,
93 void *talloc_ctx)
95 if (param.len && !ambiguous_param) {
96 char * const enable[] = { "yes", "on", "ja", "si", "igen", "y", "j",
97 "i", "tak", "ja", "true", "1" };
98 for (int i = 0; i < sizeof(enable) / sizeof(enable[0]); i++) {
99 if (!bstrcasecmp0(param, enable[i])) {
100 if (dst)
101 VAL(dst) = opt->max;
102 return 1;
105 char * const disable[] = { "no", "off", "nein", "nicht", "nem", "n",
106 "nie", "nej", "false", "0" };
107 for (int i = 0; i < sizeof(disable) / sizeof(disable[0]); i++) {
108 if (!bstrcasecmp0(param, disable[i])) {
109 if (dst)
110 VAL(dst) = opt->min;
111 return 1;
114 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
115 "Invalid parameter for %.*s flag: %.*s\n",
116 BSTR_P(name), BSTR_P(param));
117 return M_OPT_INVALID;
118 } else {
119 if (dst)
120 VAL(dst) = opt->max;
121 return 0;
125 static char *print_flag(const m_option_t *opt, const void *val)
127 if (VAL(val) == opt->min)
128 return talloc_strdup(NULL, "no");
129 else
130 return talloc_strdup(NULL, "yes");
133 const m_option_type_t m_option_type_flag = {
134 // need yes or no in config files
135 .name = "Flag",
136 .size = sizeof(int),
137 .parse = parse_flag,
138 .print = print_flag,
139 .copy = copy_opt,
142 // Integer
144 static int parse_longlong(const m_option_t *opt, struct bstr name,
145 struct bstr param, bool ambiguous_param, void *dst)
147 if (param.len == 0)
148 return M_OPT_MISSING_PARAM;
150 struct bstr rest;
151 long long tmp_int = bstrtoll(param, &rest, 10);
152 if (rest.len)
153 tmp_int = bstrtoll(param, &rest, 0);
154 if (rest.len) {
155 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
156 "The %.*s option must be an integer: %.*s\n",
157 BSTR_P(name), BSTR_P(param));
158 return M_OPT_INVALID;
161 if ((opt->flags & M_OPT_MIN) && (tmp_int < opt->min)) {
162 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
163 "The %.*s option must be >= %d: %.*s\n",
164 BSTR_P(name), (int) opt->min, BSTR_P(param));
165 return M_OPT_OUT_OF_RANGE;
168 if ((opt->flags & M_OPT_MAX) && (tmp_int > opt->max)) {
169 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
170 "The %.*s option must be <= %d: %.*s\n",
171 BSTR_P(name), (int) opt->max, BSTR_P(param));
172 return M_OPT_OUT_OF_RANGE;
175 if (dst)
176 *(long long *)dst = tmp_int;
178 return 1;
181 static int parse_int(const m_option_t *opt, struct bstr name,
182 struct bstr param, bool ambiguous_param, void *dst,
183 void *talloc_ctx)
185 long long tmp;
186 int r = parse_longlong(opt, name, param, false, &tmp);
187 if (r >= 0 && dst)
188 *(int *)dst = tmp;
189 return r;
192 static int parse_int64(const m_option_t *opt, struct bstr name,
193 struct bstr param, bool ambiguous_param, void *dst,
194 void *talloc_ctx)
196 long long tmp;
197 int r = parse_longlong(opt, name, param, false, &tmp);
198 if (r >= 0 && dst)
199 *(int64_t *)dst = tmp;
200 return r;
204 static char *print_int(const m_option_t *opt, const void *val)
206 if (opt->type->size == sizeof(int64_t))
207 return talloc_asprintf(NULL, "%"PRId64, *(const int64_t *)val);
208 return talloc_asprintf(NULL, "%d", VAL(val));
211 const m_option_type_t m_option_type_int = {
212 .name = "Integer",
213 .size = sizeof(int),
214 .parse = parse_int,
215 .print = print_int,
216 .copy = copy_opt,
219 const m_option_type_t m_option_type_int64 = {
220 .name = "Integer64",
221 .size = sizeof(int64_t),
222 .parse = parse_int64,
223 .print = print_int,
224 .copy = copy_opt,
227 static int parse_intpair(const struct m_option *opt, struct bstr name,
228 struct bstr param, bool ambiguous_param, void *dst,
229 void *talloc_ctx)
231 if (param.len == 0)
232 return M_OPT_MISSING_PARAM;
234 struct bstr s = param;
235 int end = -1;
236 int start = bstrtoll(s, &s, 10);
237 if (s.len == param.len)
238 goto bad;
239 if (s.len > 0) {
240 if (!bstr_startswith0(s, "-"))
241 goto bad;
242 s = bstr_cut(s, 1);
244 if (s.len > 0)
245 end = bstrtoll(s, &s, 10);
246 if (s.len > 0)
247 goto bad;
249 if (dst) {
250 int *p = dst;
251 p[0] = start;
252 p[1] = end;
255 return 1;
257 bad:
258 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid integer range "
259 "specification for option %.*s: %.*s\n",
260 BSTR_P(name), BSTR_P(param));
261 return M_OPT_INVALID;
264 const struct m_option_type m_option_type_intpair = {
265 .name = "Int[-Int]",
266 .size = sizeof(int[2]),
267 .parse = parse_intpair,
268 .copy = copy_opt,
271 static int parse_choice(const struct m_option *opt, struct bstr name,
272 struct bstr param, bool ambiguous_param, void *dst,
273 void *talloc_ctx)
275 if (param.len == 0)
276 return M_OPT_MISSING_PARAM;
278 struct m_opt_choice_alternatives *alt;
279 for (alt = opt->priv; alt->name; alt++)
280 if (!bstrcasecmp0(param, alt->name))
281 break;
282 if (!alt->name) {
283 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
284 "Invalid value for option %.*s: %.*s\n",
285 BSTR_P(name), BSTR_P(param));
286 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Valid values are:");
287 for (alt = opt->priv; alt->name; alt++)
288 mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", alt->name);
289 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
290 return M_OPT_INVALID;
292 if (dst)
293 *(int *)dst = alt->value;
295 return 1;
298 static char *print_choice(const m_option_t *opt, const void *val)
300 int v = *(int *)val;
301 struct m_opt_choice_alternatives *alt;
302 for (alt = opt->priv; alt->name; alt++)
303 if (alt->value == v)
304 return talloc_strdup(NULL, alt->name);
305 abort();
308 const struct m_option_type m_option_type_choice = {
309 .name = "String", // same as arbitrary strings in option list for now
310 .size = sizeof(int),
311 .parse = parse_choice,
312 .print = print_choice,
313 .copy = copy_opt,
316 // Float
318 #undef VAL
319 #define VAL(x) (*(double *)(x))
321 static int parse_double(const m_option_t *opt, struct bstr name,
322 struct bstr param, bool ambiguous_param, void *dst,
323 void *talloc_ctx)
325 if (param.len == 0)
326 return M_OPT_MISSING_PARAM;
328 struct bstr rest;
329 double tmp_float = bstrtod(param, &rest);
331 switch (rest.len ? rest.start[0] : 0) {
332 case ':':
333 case '/':
334 tmp_float /= bstrtod(bstr_cut(rest, 1), &rest);
335 break;
336 case '.':
337 case ',':
338 /* we also handle floats specified with
339 * non-locale decimal point ::atmos
341 rest = bstr_cut(rest, 1);
342 if (tmp_float < 0)
343 tmp_float -= 1.0 / pow(10, rest.len) * bstrtod(rest, &rest);
344 else
345 tmp_float += 1.0 / pow(10, rest.len) * bstrtod(rest, &rest);
346 break;
349 if (rest.len) {
350 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
351 "The %.*s option must be a floating point number or a "
352 "ratio (numerator[:/]denominator): %.*s\n",
353 BSTR_P(name), BSTR_P(param));
354 return M_OPT_INVALID;
357 if (opt->flags & M_OPT_MIN)
358 if (tmp_float < opt->min) {
359 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
360 "The %.*s option must be >= %f: %.*s\n",
361 BSTR_P(name), opt->min, BSTR_P(param));
362 return M_OPT_OUT_OF_RANGE;
365 if (opt->flags & M_OPT_MAX)
366 if (tmp_float > opt->max) {
367 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
368 "The %.*s option must be <= %f: %.*s\n",
369 BSTR_P(name), opt->max, BSTR_P(param));
370 return M_OPT_OUT_OF_RANGE;
373 if (dst)
374 VAL(dst) = tmp_float;
375 return 1;
378 static char *print_double(const m_option_t *opt, const void *val)
380 opt = NULL;
381 return talloc_asprintf(NULL, "%f", VAL(val));
384 const m_option_type_t m_option_type_double = {
385 // double precision float or ratio (numerator[:/]denominator)
386 .name = "Double",
387 .size = sizeof(double),
388 .parse = parse_double,
389 .print = print_double,
390 .copy = copy_opt,
393 #undef VAL
394 #define VAL(x) (*(float *)(x))
396 static int parse_float(const m_option_t *opt, struct bstr name,
397 struct bstr param, bool ambiguous_param, void *dst,
398 void *talloc_ctx)
400 double tmp;
401 int r = parse_double(opt, name, param, false, &tmp, NULL);
402 if (r == 1 && dst)
403 VAL(dst) = tmp;
404 return r;
407 static char *print_float(const m_option_t *opt, const void *val)
409 opt = NULL;
410 return talloc_asprintf(NULL, "%f", VAL(val));
413 const m_option_type_t m_option_type_float = {
414 // floating point number or ratio (numerator[:/]denominator)
415 .name = "Float",
416 .size = sizeof(float),
417 .parse = parse_float,
418 .print = print_float,
419 .copy = copy_opt,
422 ///////////// Position
423 #undef VAL
424 #define VAL(x) (*(off_t *)(x))
426 static int parse_position(const m_option_t *opt, struct bstr name,
427 struct bstr param, bool ambiguous_param, void *dst,
428 void *talloc_ctx)
430 long long tmp;
431 int r = parse_longlong(opt, name, param, false, &tmp);
432 if (r >= 0 && dst)
433 *(off_t *)dst = tmp;
434 return r;
437 static char *print_position(const m_option_t *opt, const void *val)
439 return talloc_asprintf(NULL, "%"PRId64, (int64_t)VAL(val));
442 const m_option_type_t m_option_type_position = {
443 // Integer (off_t)
444 .name = "Position",
445 .size = sizeof(off_t),
446 .parse = parse_position,
447 .print = print_position,
448 .copy = copy_opt,
452 ///////////// String
454 #undef VAL
455 #define VAL(x) (*(char **)(x))
457 static int parse_str(const m_option_t *opt, struct bstr name,
458 struct bstr param, bool ambiguous_param, void *dst,
459 void *talloc_ctx)
461 if (param.start == NULL)
462 return M_OPT_MISSING_PARAM;
464 if ((opt->flags & M_OPT_MIN) && (param.len < opt->min)) {
465 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
466 "Parameter must be >= %d chars: %.*s\n",
467 (int) opt->min, BSTR_P(param));
468 return M_OPT_OUT_OF_RANGE;
471 if ((opt->flags & M_OPT_MAX) && (param.len > opt->max)) {
472 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
473 "Parameter must be <= %d chars: %.*s\n",
474 (int) opt->max, BSTR_P(param));
475 return M_OPT_OUT_OF_RANGE;
478 if (dst) {
479 talloc_free(VAL(dst));
480 VAL(dst) = bstrdup0(talloc_ctx, param);
483 return 1;
487 static char *print_str(const m_option_t *opt, const void *val)
489 return (val && VAL(val)) ? talloc_strdup(NULL, VAL(val)) : NULL;
492 static void copy_str(const m_option_t *opt, void *dst, const void *src,
493 void *talloc_ctx)
495 if (dst && src) {
496 talloc_free(VAL(dst));
497 VAL(dst) = talloc_strdup(talloc_ctx, VAL(src));
501 static void free_str(void *src)
503 if (src && VAL(src)) {
504 talloc_free(VAL(src));
505 VAL(src) = NULL;
509 const m_option_type_t m_option_type_string = {
510 .name = "String",
511 .size = sizeof(char *),
512 .flags = M_OPT_TYPE_DYNAMIC,
513 .parse = parse_str,
514 .print = print_str,
515 .copy = copy_str,
516 .free = free_str,
519 //////////// String list
521 #undef VAL
522 #define VAL(x) (*(char ***)(x))
524 #define OP_NONE 0
525 #define OP_ADD 1
526 #define OP_PRE 2
527 #define OP_DEL 3
528 #define OP_CLR 4
530 static void free_str_list(void *dst)
532 char **d;
533 int i;
535 if (!dst || !VAL(dst))
536 return;
537 d = VAL(dst);
539 for (i = 0; d[i] != NULL; i++)
540 talloc_free(d[i]);
541 talloc_free(d);
542 VAL(dst) = NULL;
545 static int str_list_add(char **add, int n, void *dst, int pre, void *talloc_ctx)
547 char **lst = VAL(dst);
548 int ln;
550 if (!dst)
551 return M_OPT_PARSER_ERR;
552 lst = VAL(dst);
554 for (ln = 0; lst && lst[ln]; ln++)
555 /**/;
557 lst = talloc_realloc(NULL, lst, char *, n + ln + 1);
559 if (pre) {
560 memmove(&lst[n], lst, ln * sizeof(char *));
561 memcpy(lst, add, n * sizeof(char *));
562 } else
563 memcpy(&lst[ln], add, n * sizeof(char *));
564 // (re-)add NULL-termination
565 lst[ln + n] = NULL;
567 talloc_free(add);
569 VAL(dst) = lst;
571 return 1;
574 static int str_list_del(char **del, int n, void *dst)
576 char **lst, *ep;
577 int i, ln, s;
578 long idx;
580 if (!dst)
581 return M_OPT_PARSER_ERR;
582 lst = VAL(dst);
584 for (ln = 0; lst && lst[ln]; ln++)
585 /**/;
586 s = ln;
588 for (i = 0; del[i] != NULL; i++) {
589 idx = strtol(del[i], &ep, 0);
590 if (*ep) {
591 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid index: %s\n", del[i]);
592 talloc_free(del[i]);
593 continue;
595 talloc_free(del[i]);
596 if (idx < 0 || idx >= ln) {
597 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
598 "Index %ld is out of range.\n", idx);
599 continue;
600 } else if (!lst[idx])
601 continue;
602 talloc_free(lst[idx]);
603 lst[idx] = NULL;
604 s--;
606 talloc_free(del);
608 if (s == 0) {
609 talloc_free(lst);
610 VAL(dst) = NULL;
611 return 1;
614 // Don't bother shrinking the list allocation
615 for (i = 0, n = 0; i < ln; i++) {
616 if (!lst[i])
617 continue;
618 lst[n] = lst[i];
619 n++;
621 lst[s] = NULL;
623 return 1;
626 static struct bstr get_nextsep(struct bstr *ptr, char sep, bool modify)
628 struct bstr str = *ptr;
629 struct bstr orig = str;
630 for (;;) {
631 int idx = bstrchr(str, sep);
632 if (idx > 0 && str.start[idx - 1] == '\\') {
633 if (modify) {
634 memmove(str.start + idx - 1, str.start + idx, str.len - idx);
635 str.len--;
636 str = bstr_cut(str, idx);
637 } else
638 str = bstr_cut(str, idx + 1);
639 } else {
640 str = bstr_cut(str, idx < 0 ? str.len : idx);
641 break;
644 *ptr = str;
645 return bstr_splice(orig, 0, str.start - orig.start);
648 static int parse_str_list(const m_option_t *opt, struct bstr name,
649 struct bstr param, bool ambiguous_param, void *dst,
650 void *talloc_ctx)
652 char **res;
653 int op = OP_NONE;
654 int len = strlen(opt->name);
655 if (opt->name[len - 1] == '*' && (name.len > len - 1)) {
656 struct bstr suffix = bstr_cut(name, len - 1);
657 if (bstrcasecmp0(suffix, "-add") == 0)
658 op = OP_ADD;
659 else if (bstrcasecmp0(suffix, "-pre") == 0)
660 op = OP_PRE;
661 else if (bstrcasecmp0(suffix, "-del") == 0)
662 op = OP_DEL;
663 else if (bstrcasecmp0(suffix, "-clr") == 0)
664 op = OP_CLR;
665 else
666 return M_OPT_UNKNOWN;
669 // Clear the list ??
670 if (op == OP_CLR) {
671 if (dst)
672 free_str_list(dst);
673 return 0;
676 // All other ops need a param
677 if (param.len == 0)
678 return M_OPT_MISSING_PARAM;
680 // custom type for "profile" calls this but uses ->priv for something else
681 char separator = opt->type == &m_option_type_string_list && opt->priv ?
682 *(char *)opt->priv : OPTION_LIST_SEPARATOR;
683 int n = 0;
684 struct bstr str = param;
685 while (str.len) {
686 get_nextsep(&str, separator, 0);
687 str = bstr_cut(str, 1);
688 n++;
690 if (n == 0)
691 return M_OPT_INVALID;
692 if (((opt->flags & M_OPT_MIN) && (n < opt->min)) ||
693 ((opt->flags & M_OPT_MAX) && (n > opt->max)))
694 return M_OPT_OUT_OF_RANGE;
696 if (!dst)
697 return 1;
699 res = talloc_array(talloc_ctx, char *, n + 2);
700 str = bstrdup(NULL, param);
701 char *ptr = str.start;
702 n = 0;
704 while (1) {
705 struct bstr el = get_nextsep(&str, separator, 1);
706 res[n] = bstrdup0(talloc_ctx, el);
707 n++;
708 if (!str.len)
709 break;
710 str = bstr_cut(str, 1);
712 res[n] = NULL;
713 talloc_free(ptr);
715 switch (op) {
716 case OP_ADD:
717 return str_list_add(res, n, dst, 0, talloc_ctx);
718 case OP_PRE:
719 return str_list_add(res, n, dst, 1, talloc_ctx);
720 case OP_DEL:
721 return str_list_del(res, n, dst);
724 if (VAL(dst))
725 free_str_list(dst);
726 VAL(dst) = res;
728 return 1;
731 static void copy_str_list(const m_option_t *opt, void *dst, const void *src,
732 void *talloc_ctx)
734 int n;
735 char **d, **s;
737 if (!(dst && src))
738 return;
739 s = VAL(src);
741 if (VAL(dst))
742 free_str_list(dst);
744 if (!s) {
745 VAL(dst) = NULL;
746 return;
749 for (n = 0; s[n] != NULL; n++)
750 /* NOTHING */;
751 d = talloc_array(talloc_ctx, char *, n + 1);
752 for (; n >= 0; n--)
753 d[n] = talloc_strdup(talloc_ctx, s[n]);
755 VAL(dst) = d;
758 static char *print_str_list(const m_option_t *opt, const void *src)
760 char **lst = NULL;
761 char *ret = NULL;
763 if (!(src && VAL(src)))
764 return NULL;
765 lst = VAL(src);
767 for (int i = 0; lst[i]; i++) {
768 if (ret)
769 ret = talloc_strdup_append_buffer(ret, ",");
770 ret = talloc_strdup_append_buffer(ret, lst[i]);
772 return ret;
775 const m_option_type_t m_option_type_string_list = {
776 /* A list of strings separated by ','.
777 * Option with a name ending in '*' permits using the following suffixes:
778 * -add: Add the given parameters at the end of the list.
779 * -pre: Add the given parameters at the beginning of the list.
780 * -del: Remove the entry at the given indices.
781 * -clr: Clear the list.
782 * e.g: -vf-add flip,mirror -vf-del 2,5
784 .name = "String list",
785 .size = sizeof(char **),
786 .flags = M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
787 .parse = parse_str_list,
788 .print = print_str_list,
789 .copy = copy_str_list,
790 .free = free_str_list,
794 /////////////////// Print
796 static int parse_print(const m_option_t *opt, struct bstr name,
797 struct bstr param, bool ambiguous_param, void *dst,
798 void *talloc_ctx)
800 if (opt->type == CONF_TYPE_PRINT_INDIRECT)
801 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s", *(char **) opt->p);
802 else if (opt->type == CONF_TYPE_PRINT_FUNC) {
803 char *name0 = bstrdup0(NULL, name);
804 char *param0 = bstrdup0(NULL, param);
805 int r = ((m_opt_func_full_t) opt->p)(opt, name0, param0);
806 talloc_free(name0);
807 talloc_free(param0);
808 return r;
809 } else
810 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s", mp_gtext(opt->p));
812 if (opt->priv == NULL)
813 return M_OPT_EXIT;
814 return 0;
817 const m_option_type_t m_option_type_print = {
818 .name = "Print",
819 .parse = parse_print,
822 const m_option_type_t m_option_type_print_indirect = {
823 .name = "Print",
824 .parse = parse_print,
827 const m_option_type_t m_option_type_print_func = {
828 .name = "Print",
829 .flags = M_OPT_TYPE_ALLOW_WILDCARD,
830 .parse = parse_print,
834 /////////////////////// Subconfig
835 #undef VAL
836 #define VAL(x) (*(char ***)(x))
838 static int parse_subconf(const m_option_t *opt, struct bstr name,
839 struct bstr param, bool ambiguous_param, void *dst,
840 void *talloc_ctx)
842 int nr = 0;
843 char **lst = NULL;
845 if (param.len == 0)
846 return M_OPT_MISSING_PARAM;
848 struct bstr p = param;
850 while (p.len) {
851 int optlen = bstrcspn(p, ":=");
852 struct bstr subopt = bstr_splice(p, 0, optlen);
853 struct bstr subparam = bstr(NULL);
854 p = bstr_cut(p, optlen);
855 if (bstr_startswith0(p, "=")) {
856 p = bstr_cut(p, 1);
857 if (bstr_startswith0(p, "\"")) {
858 p = bstr_cut(p, 1);
859 optlen = bstrcspn(p, "\"");
860 subparam = bstr_splice(p, 0, optlen);
861 p = bstr_cut(p, optlen);
862 if (!bstr_startswith0(p, "\"")) {
863 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
864 "Terminating '\"' missing for '%.*s'\n",
865 BSTR_P(subopt));
866 return M_OPT_INVALID;
868 p = bstr_cut(p, 1);
869 } else if (bstr_startswith0(p, "%")) {
870 p = bstr_cut(p, 1);
871 optlen = bstrtoll(p, &p, 0);
872 if (!bstr_startswith0(p, "%") || (optlen > p.len - 1)) {
873 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
874 "Invalid length %d for '%.*s'\n",
875 optlen, BSTR_P(subopt));
876 return M_OPT_INVALID;
878 subparam = bstr_splice(p, 1, optlen + 1);
879 p = bstr_cut(p, optlen + 1);
880 } else {
881 optlen = bstrcspn(p, ":");
882 subparam = bstr_splice(p, 0, optlen);
883 p = bstr_cut(p, optlen);
886 if (bstr_startswith0(p, ":"))
887 p = bstr_cut(p, 1);
888 else if (p.len > 0) {
889 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
890 "Incorrect termination for '%.*s'\n", BSTR_P(subopt));
891 return M_OPT_INVALID;
894 if (dst) {
895 lst = talloc_realloc(NULL, lst, char *, 2 * (nr + 2));
896 lst[2 * nr] = bstrdup0(lst, subopt);
897 lst[2 * nr + 1] = bstrdup0(lst, subparam);
898 memset(&lst[2 * (nr + 1)], 0, 2 * sizeof(char *));
899 nr++;
903 if (dst)
904 VAL(dst) = lst;
906 return 1;
909 const m_option_type_t m_option_type_subconfig = {
910 // The syntax is -option opt1=foo:flag:opt2=blah
911 .name = "Subconfig",
912 .flags = M_OPT_TYPE_HAS_CHILD,
913 .parse = parse_subconf,
916 #include "libmpcodecs/img_format.h"
918 /* FIXME: snyc with img_format.h */
919 static struct {
920 const char *name;
921 unsigned int fmt;
922 } mp_imgfmt_list[] = {
923 {"444p16le", IMGFMT_444P16_LE},
924 {"444p16be", IMGFMT_444P16_BE},
925 {"444p10le", IMGFMT_444P10_LE},
926 {"444p10be", IMGFMT_444P10_BE},
927 {"444p9le", IMGFMT_444P9_LE},
928 {"444p9be", IMGFMT_444P9_BE},
929 {"422p16le", IMGFMT_422P16_LE},
930 {"422p16be", IMGFMT_422P16_BE},
931 {"422p10le", IMGFMT_422P10_LE},
932 {"422p10be", IMGFMT_422P10_BE},
933 {"420p16le", IMGFMT_420P16_LE},
934 {"420p16be", IMGFMT_420P16_BE},
935 {"420p10le", IMGFMT_420P10_LE},
936 {"420p10be", IMGFMT_420P10_BE},
937 {"420p9le", IMGFMT_420P9_LE},
938 {"420p9be", IMGFMT_420P9_BE},
939 {"444p16", IMGFMT_444P16},
940 {"444p10", IMGFMT_444P10},
941 {"444p9", IMGFMT_444P9},
942 {"422p16", IMGFMT_422P16},
943 {"422p10", IMGFMT_422P10},
944 {"420p10", IMGFMT_420P10},
945 {"420p9", IMGFMT_420P9},
946 {"420p16", IMGFMT_420P16},
947 {"420a", IMGFMT_420A},
948 {"444p", IMGFMT_444P},
949 {"422p", IMGFMT_422P},
950 {"411p", IMGFMT_411P},
951 {"440p", IMGFMT_440P},
952 {"yuy2", IMGFMT_YUY2},
953 {"yvyu", IMGFMT_YVYU},
954 {"uyvy", IMGFMT_UYVY},
955 {"yvu9", IMGFMT_YVU9},
956 {"if09", IMGFMT_IF09},
957 {"yv12", IMGFMT_YV12},
958 {"i420", IMGFMT_I420},
959 {"iyuv", IMGFMT_IYUV},
960 {"clpl", IMGFMT_CLPL},
961 {"hm12", IMGFMT_HM12},
962 {"y800", IMGFMT_Y800},
963 {"y8", IMGFMT_Y8},
964 {"nv12", IMGFMT_NV12},
965 {"nv21", IMGFMT_NV21},
966 {"bgr24", IMGFMT_BGR24},
967 {"bgr32", IMGFMT_BGR32},
968 {"bgr16", IMGFMT_BGR16},
969 {"bgr15", IMGFMT_BGR15},
970 {"bgr12", IMGFMT_BGR12},
971 {"bgr8", IMGFMT_BGR8},
972 {"bgr4", IMGFMT_BGR4},
973 {"bg4b", IMGFMT_BG4B},
974 {"bgr1", IMGFMT_BGR1},
975 {"rgb48be", IMGFMT_RGB48BE},
976 {"rgb48le", IMGFMT_RGB48LE},
977 {"rgb48ne", IMGFMT_RGB48NE},
978 {"rgb24", IMGFMT_RGB24},
979 {"rgb32", IMGFMT_RGB32},
980 {"rgb16", IMGFMT_RGB16},
981 {"rgb15", IMGFMT_RGB15},
982 {"rgb12", IMGFMT_RGB12},
983 {"rgb8", IMGFMT_RGB8},
984 {"rgb4", IMGFMT_RGB4},
985 {"rg4b", IMGFMT_RG4B},
986 {"rgb1", IMGFMT_RGB1},
987 {"rgba", IMGFMT_RGBA},
988 {"argb", IMGFMT_ARGB},
989 {"bgra", IMGFMT_BGRA},
990 {"abgr", IMGFMT_ABGR},
991 {"mjpeg", IMGFMT_MJPEG},
992 {"mjpg", IMGFMT_MJPEG},
993 { NULL, 0 }
996 static int parse_imgfmt(const m_option_t *opt, struct bstr name,
997 struct bstr param, bool ambiguous_param, void *dst,
998 void *talloc_ctx)
1000 uint32_t fmt = 0;
1001 int i;
1003 if (param.len == 0)
1004 return M_OPT_MISSING_PARAM;
1006 if (!bstrcmp0(param, "help")) {
1007 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available formats:");
1008 for (i = 0; mp_imgfmt_list[i].name; i++)
1009 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %s", mp_imgfmt_list[i].name);
1010 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1011 return M_OPT_EXIT - 1;
1014 if (bstr_startswith0(param, "0x"))
1015 fmt = bstrtoll(param, NULL, 16);
1016 else {
1017 for (i = 0; mp_imgfmt_list[i].name; i++) {
1018 if (!bstrcasecmp0(param, mp_imgfmt_list[i].name)) {
1019 fmt = mp_imgfmt_list[i].fmt;
1020 break;
1023 if (!mp_imgfmt_list[i].name) {
1024 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1025 "Option %.*s: unknown format name: '%.*s'\n",
1026 BSTR_P(name), BSTR_P(param));
1027 return M_OPT_INVALID;
1031 if (dst)
1032 *((uint32_t *)dst) = fmt;
1034 return 1;
1037 const m_option_type_t m_option_type_imgfmt = {
1038 // Please report any missing colorspaces
1039 .name = "Image format",
1040 .size = sizeof(uint32_t),
1041 .parse = parse_imgfmt,
1042 .copy = copy_opt,
1045 #include "libaf/af_format.h"
1047 /* FIXME: snyc with af_format.h */
1048 static struct {
1049 const char *name;
1050 unsigned int fmt;
1051 } mp_afmt_list[] = {
1052 // SPECIAL
1053 {"mulaw", AF_FORMAT_MU_LAW},
1054 {"alaw", AF_FORMAT_A_LAW},
1055 {"mpeg2", AF_FORMAT_MPEG2},
1056 {"ac3le", AF_FORMAT_AC3_LE},
1057 {"ac3be", AF_FORMAT_AC3_BE},
1058 {"ac3ne", AF_FORMAT_AC3_NE},
1059 {"imaadpcm", AF_FORMAT_IMA_ADPCM},
1060 // ORDINARY
1061 {"u8", AF_FORMAT_U8},
1062 {"s8", AF_FORMAT_S8},
1063 {"u16le", AF_FORMAT_U16_LE},
1064 {"u16be", AF_FORMAT_U16_BE},
1065 {"u16ne", AF_FORMAT_U16_NE},
1066 {"s16le", AF_FORMAT_S16_LE},
1067 {"s16be", AF_FORMAT_S16_BE},
1068 {"s16ne", AF_FORMAT_S16_NE},
1069 {"u24le", AF_FORMAT_U24_LE},
1070 {"u24be", AF_FORMAT_U24_BE},
1071 {"u24ne", AF_FORMAT_U24_NE},
1072 {"s24le", AF_FORMAT_S24_LE},
1073 {"s24be", AF_FORMAT_S24_BE},
1074 {"s24ne", AF_FORMAT_S24_NE},
1075 {"u32le", AF_FORMAT_U32_LE},
1076 {"u32be", AF_FORMAT_U32_BE},
1077 {"u32ne", AF_FORMAT_U32_NE},
1078 {"s32le", AF_FORMAT_S32_LE},
1079 {"s32be", AF_FORMAT_S32_BE},
1080 {"s32ne", AF_FORMAT_S32_NE},
1081 {"floatle", AF_FORMAT_FLOAT_LE},
1082 {"floatbe", AF_FORMAT_FLOAT_BE},
1083 {"floatne", AF_FORMAT_FLOAT_NE},
1084 { NULL, 0 }
1087 static int parse_afmt(const m_option_t *opt, struct bstr name,
1088 struct bstr param, bool ambiguous_param, void *dst,
1089 void *talloc_ctx)
1091 uint32_t fmt = 0;
1092 int i;
1094 if (param.len == 0)
1095 return M_OPT_MISSING_PARAM;
1097 if (!bstrcmp0(param, "help")) {
1098 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available formats:");
1099 for (i = 0; mp_afmt_list[i].name; i++)
1100 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %s", mp_afmt_list[i].name);
1101 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1102 return M_OPT_EXIT - 1;
1105 if (bstr_startswith0(param, "0x"))
1106 fmt = bstrtoll(param, NULL, 16);
1107 else {
1108 for (i = 0; mp_afmt_list[i].name; i++) {
1109 if (!bstrcasecmp0(param, mp_afmt_list[i].name)) {
1110 fmt = mp_afmt_list[i].fmt;
1111 break;
1114 if (!mp_afmt_list[i].name) {
1115 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1116 "Option %.*s: unknown format name: '%.*s'\n",
1117 BSTR_P(name), BSTR_P(param));
1118 return M_OPT_INVALID;
1122 if (dst)
1123 *((uint32_t *)dst) = fmt;
1125 return 1;
1128 const m_option_type_t m_option_type_afmt = {
1129 // Please report any missing formats
1130 .name = "Audio format",
1131 .size = sizeof(uint32_t),
1132 .parse = parse_afmt,
1133 .copy = copy_opt,
1137 static int parse_timestring(struct bstr str, double *time, char endchar)
1139 int a, b, len;
1140 double d;
1141 *time = 0; /* ensure initialization for error cases */
1142 if (bstr_sscanf(str, "%d:%d:%lf%n", &a, &b, &d, &len) >= 3)
1143 *time = 3600 * a + 60 * b + d;
1144 else if (bstr_sscanf(str, "%d:%lf%n", &a, &d, &len) >= 2)
1145 *time = 60 * a + d;
1146 else if (bstr_sscanf(str, "%lf%n", &d, &len) >= 1)
1147 *time = d;
1148 else
1149 return 0; /* unsupported time format */
1150 if (len < str.len && str.start[len] != endchar)
1151 return 0; /* invalid extra characters at the end */
1152 return len;
1156 static int parse_time(const m_option_t *opt, struct bstr name,
1157 struct bstr param, bool ambiguous_param, void *dst,
1158 void *talloc_ctx)
1160 double time;
1162 if (param.len == 0)
1163 return M_OPT_MISSING_PARAM;
1165 if (!parse_timestring(param, &time, 0)) {
1166 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: invalid time: '%.*s'\n",
1167 BSTR_P(name), BSTR_P(param));
1168 return M_OPT_INVALID;
1171 if (dst)
1172 *(double *)dst = time;
1173 return 1;
1176 const m_option_type_t m_option_type_time = {
1177 .name = "Time",
1178 .size = sizeof(double),
1179 .parse = parse_time,
1180 .print = print_double,
1181 .copy = copy_opt,
1185 // Time or size (-endpos)
1187 static int parse_time_size(const m_option_t *opt, struct bstr name,
1188 struct bstr param, bool ambiguous_param, void *dst,
1189 void *talloc_ctx)
1191 m_time_size_t ts;
1192 char unit[4];
1193 double end_at;
1195 if (param.len == 0)
1196 return M_OPT_MISSING_PARAM;
1198 ts.pos = 0;
1199 /* End at size parsing */
1200 if (bstr_sscanf(param, "%lf%3s", &end_at, unit) == 2) {
1201 ts.type = END_AT_SIZE;
1202 if (!strcasecmp(unit, "b"))
1204 else if (!strcasecmp(unit, "kb"))
1205 end_at *= 1024;
1206 else if (!strcasecmp(unit, "mb"))
1207 end_at *= 1024 * 1024;
1208 else if (!strcasecmp(unit, "gb"))
1209 end_at *= 1024 * 1024 * 1024;
1210 else
1211 ts.type = END_AT_NONE;
1213 if (ts.type == END_AT_SIZE) {
1214 ts.pos = end_at;
1215 goto out;
1219 /* End at time parsing. This has to be last because the parsing accepts
1220 * even a number followed by garbage */
1221 if (!parse_timestring(param, &end_at, 0)) {
1222 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1223 "Option %.*s: invalid time or size: '%.*s'\n",
1224 BSTR_P(name), BSTR_P(param));
1225 return M_OPT_INVALID;
1228 ts.type = END_AT_TIME;
1229 ts.pos = end_at;
1230 out:
1231 if (dst)
1232 *(m_time_size_t *)dst = ts;
1233 return 1;
1236 const m_option_type_t m_option_type_time_size = {
1237 .name = "Time or size",
1238 .size = sizeof(m_time_size_t),
1239 .parse = parse_time_size,
1240 .copy = copy_opt,
1244 //// Objects (i.e. filters, etc) settings
1246 #include "m_struct.h"
1248 #undef VAL
1249 #define VAL(x) (*(m_obj_settings_t **)(x))
1251 static int find_obj_desc(struct bstr name, const m_obj_list_t *l,
1252 const m_struct_t **ret)
1254 int i;
1255 char *n;
1257 for (i = 0; l->list[i]; i++) {
1258 n = M_ST_MB(char *, l->list[i], l->name_off);
1259 if (!bstrcmp0(name, n)) {
1260 *ret = M_ST_MB(m_struct_t *, l->list[i], l->desc_off);
1261 return 1;
1264 return 0;
1267 static int get_obj_param(struct bstr opt_name, struct bstr obj_name,
1268 const m_struct_t *desc, struct bstr str, int *nold,
1269 int oldmax, char **dst)
1271 const m_option_t *opt;
1272 int r;
1274 int eq = bstrchr(str, '=');
1276 if (eq > 0) { // eq == 0 ignored
1277 struct bstr p = bstr_cut(str, eq + 1);
1278 str = bstr_splice(str, 0, eq);
1279 opt = m_option_list_findb(desc->fields, str);
1280 if (!opt) {
1281 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1282 "Option %.*s: %.*s doesn't have a %.*s parameter.\n",
1283 BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(str));
1284 return M_OPT_UNKNOWN;
1286 r = m_option_parse(opt, str, p, false, NULL);
1287 if (r < 0) {
1288 if (r > M_OPT_EXIT)
1289 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: "
1290 "Error while parsing %.*s parameter %.*s (%.*s)\n",
1291 BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(str),
1292 BSTR_P(p));
1293 return r;
1295 if (dst) {
1296 dst[0] = bstrdup0(NULL, str);
1297 dst[1] = bstrdup0(NULL, p);
1299 } else {
1300 if ((*nold) >= oldmax) {
1301 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: %.*s has only %d params, so you can't give more than %d unnamed params.\n",
1302 BSTR_P(opt_name), BSTR_P(obj_name), oldmax, oldmax);
1303 return M_OPT_OUT_OF_RANGE;
1305 opt = &desc->fields[(*nold)];
1306 r = m_option_parse(opt, bstr(opt->name), str, false, NULL);
1307 if (r < 0) {
1308 if (r > M_OPT_EXIT)
1309 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: "
1310 "Error while parsing %.*s parameter %s (%.*s)\n",
1311 BSTR_P(opt_name), BSTR_P(obj_name), opt->name,
1312 BSTR_P(str));
1313 return r;
1315 if (dst) {
1316 dst[0] = talloc_strdup(NULL, opt->name);
1317 dst[1] = bstrdup0(NULL, str);
1319 (*nold)++;
1321 return 1;
1324 static int get_obj_params(struct bstr opt_name, struct bstr name,
1325 struct bstr params, const m_struct_t *desc,
1326 char separator, char ***_ret)
1328 int n = 0, nold = 0, nopts;
1329 char **ret;
1331 if (!bstrcmp0(params, "help")) { // Help
1332 char min[50], max[50];
1333 if (!desc->fields) {
1334 mp_msg(MSGT_CFGPARSER, MSGL_INFO,
1335 "%.*s doesn't have any options.\n\n", BSTR_P(name));
1336 return M_OPT_EXIT - 1;
1338 mp_msg(MSGT_CFGPARSER, MSGL_INFO,
1339 "\n Name Type Min Max\n\n");
1340 for (n = 0; desc->fields[n].name; n++) {
1341 const m_option_t *opt = &desc->fields[n];
1342 if (opt->type->flags & M_OPT_TYPE_HAS_CHILD)
1343 continue;
1344 if (opt->flags & M_OPT_MIN)
1345 sprintf(min, "%-8.0f", opt->min);
1346 else
1347 strcpy(min, "No");
1348 if (opt->flags & M_OPT_MAX)
1349 sprintf(max, "%-8.0f", opt->max);
1350 else
1351 strcpy(max, "No");
1352 mp_msg(MSGT_CFGPARSER, MSGL_INFO,
1353 " %-20.20s %-15.15s %-10.10s %-10.10s\n",
1354 opt->name, opt->type->name, min, max);
1356 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1357 return M_OPT_EXIT - 1;
1360 for (nopts = 0; desc->fields[nopts].name; nopts++)
1361 /* NOP */;
1363 // TODO : Check that each opt can be parsed
1364 struct bstr s = params;
1365 while (1) {
1366 bool end = false;
1367 int idx = bstrchr(s, separator);
1368 if (idx < 0) {
1369 idx = s.len;
1370 end = true;
1372 struct bstr field = bstr_splice(s, 0, idx);
1373 s = bstr_cut(s, idx + 1);
1374 if (field.len == 0) { // Empty field, count it and go on
1375 nold++;
1376 } else {
1377 int r = get_obj_param(opt_name, name, desc, field, &nold, nopts,
1378 NULL);
1379 if (r < 0)
1380 return r;
1381 n++;
1383 if (end)
1384 break;
1386 if (nold > nopts) {
1387 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Too many options for %.*s\n",
1388 BSTR_P(name));
1389 return M_OPT_OUT_OF_RANGE;
1391 if (!_ret) // Just test
1392 return 1;
1393 if (n == 0) // No options or only empty options
1394 return 1;
1396 ret = talloc_array(NULL, char *, (n + 2) * 2);
1397 n = nold = 0;
1398 s = params;
1400 while (s.len > 0) {
1401 int idx = bstrchr(s, separator);
1402 if (idx < 0)
1403 idx = s.len;
1404 struct bstr field = bstr_splice(s, 0, idx);
1405 s = bstr_cut(s, idx + 1);
1406 if (field.len == 0) { // Empty field, count it and go on
1407 nold++;
1408 } else {
1409 get_obj_param(opt_name, name, desc, field, &nold, nopts,
1410 &ret[n * 2]);
1411 n++;
1414 ret[n * 2] = ret[n * 2 + 1] = NULL;
1415 *_ret = ret;
1417 return 1;
1420 static int parse_obj_params(const m_option_t *opt, struct bstr name,
1421 struct bstr param, bool ambiguous_param, void *dst,
1422 void *talloc_ctx)
1424 char **opts;
1425 int r;
1426 m_obj_params_t *p = opt->priv;
1427 const m_struct_t *desc;
1429 // We need the object desc
1430 if (!p)
1431 return M_OPT_INVALID;
1433 desc = p->desc;
1434 r = get_obj_params(name, bstr(desc->name), param, desc, p->separator,
1435 dst ? &opts : NULL);
1436 if (r < 0)
1437 return r;
1438 if (!dst)
1439 return 1;
1440 if (!opts) // no arguments given
1441 return 1;
1443 for (r = 0; opts[r]; r += 2)
1444 m_struct_set(desc, dst, opts[r], bstr(opts[r + 1]));
1446 return 1;
1450 const m_option_type_t m_option_type_obj_params = {
1451 .name = "Object params",
1452 .parse = parse_obj_params,
1455 /// Some predefined types as a definition would be quite lengthy
1457 /// Span arguments
1458 static const m_span_t m_span_params_dflts = {
1459 -1, -1
1461 static const m_option_t m_span_params_fields[] = {
1462 {"start", M_ST_OFF(m_span_t, start), CONF_TYPE_INT, M_OPT_MIN, 1, 0, NULL},
1463 {"end", M_ST_OFF(m_span_t, end), CONF_TYPE_INT, M_OPT_MIN, 1, 0, NULL},
1464 { NULL, NULL, 0, 0, 0, 0, NULL }
1466 static const struct m_struct_st m_span_opts = {
1467 "m_span",
1468 sizeof(m_span_t),
1469 &m_span_params_dflts,
1470 m_span_params_fields
1472 const m_obj_params_t m_span_params_def = {
1473 &m_span_opts,
1477 static int parse_obj_settings(struct bstr opt, struct bstr str,
1478 const m_obj_list_t *list,
1479 m_obj_settings_t **_ret, int ret_n)
1481 int r;
1482 char **plist = NULL;
1483 const m_struct_t *desc;
1484 m_obj_settings_t *ret = _ret ? *_ret : NULL;
1486 struct bstr param = bstr(NULL);
1487 int idx = bstrchr(str, '=');
1488 if (idx >= 0) {
1489 param = bstr_cut(str, idx + 1);
1490 str = bstr_splice(str, 0, idx);
1493 if (!find_obj_desc(str, list, &desc)) {
1494 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: %.*s doesn't exist.\n",
1495 BSTR_P(opt), BSTR_P(str));
1496 return M_OPT_INVALID;
1499 if (param.start) {
1500 if (!desc && _ret) {
1501 if (!bstrcmp0(param, "help")) {
1502 mp_msg(MSGT_CFGPARSER, MSGL_INFO,
1503 "Option %.*s: %.*s have no option description.\n",
1504 BSTR_P(opt), BSTR_P(str));
1505 return M_OPT_EXIT - 1;
1507 plist = talloc_zero_array(NULL, char *, 4);
1508 plist[0] = talloc_strdup(NULL, "_oldargs_");
1509 plist[1] = bstrdup0(NULL, param);
1510 } else if (desc) {
1511 r = get_obj_params(opt, str, param, desc, ':',
1512 _ret ? &plist : NULL);
1513 if (r < 0)
1514 return r;
1517 if (!_ret)
1518 return 1;
1520 ret = talloc_realloc(NULL, ret, struct m_obj_settings, ret_n + 2);
1521 memset(&ret[ret_n], 0, 2 * sizeof(m_obj_settings_t));
1522 ret[ret_n].name = bstrdup0(NULL, str);
1523 ret[ret_n].attribs = plist;
1525 *_ret = ret;
1526 return 1;
1529 static int obj_settings_list_del(struct bstr opt_name, struct bstr param,
1530 bool ambiguous_param, void *dst)
1532 char **str_list = NULL;
1533 int r, i, idx_max = 0;
1534 char *rem_id = "_removed_marker_";
1535 char name[100];
1536 assert(opt_name.len < 100);
1537 memcpy(name, opt_name.start, opt_name.len);
1538 name[opt_name.len] = 0;
1539 const m_option_t list_opt = {
1540 name, NULL, CONF_TYPE_STRING_LIST,
1541 0, 0, 0, NULL
1543 m_obj_settings_t *obj_list = dst ? VAL(dst) : NULL;
1545 if (dst && !obj_list) {
1546 mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %.*s: the list is empty.\n",
1547 BSTR_P(opt_name));
1548 return 1;
1549 } else if (obj_list) {
1550 for (idx_max = 0; obj_list[idx_max].name != NULL; idx_max++)
1551 /* NOP */;
1554 r = m_option_parse(&list_opt, opt_name, param, false, &str_list);
1555 if (r < 0 || !str_list)
1556 return r;
1558 for (r = 0; str_list[r]; r++) {
1559 int id;
1560 char *endptr;
1561 id = strtol(str_list[r], &endptr, 0);
1562 if (endptr == str_list[r]) {
1563 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: invalid parameter. We need a list of integers which are the indices of the elements to remove.\n", BSTR_P(opt_name));
1564 m_option_free(&list_opt, &str_list);
1565 return M_OPT_INVALID;
1567 if (!obj_list)
1568 continue;
1569 if (id >= idx_max || id < -idx_max) {
1570 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
1571 "Option %.*s: Index %d is out of range.\n",
1572 BSTR_P(opt_name), id);
1573 continue;
1575 if (id < 0)
1576 id = idx_max + id;
1577 talloc_free(obj_list[id].name);
1578 free_str_list(&(obj_list[id].attribs));
1579 obj_list[id].name = rem_id;
1582 if (!dst) {
1583 m_option_free(&list_opt, &str_list);
1584 return 1;
1587 for (i = 0; obj_list[i].name; i++) {
1588 while (obj_list[i].name == rem_id) {
1589 memmove(&obj_list[i], &obj_list[i + 1],
1590 sizeof(m_obj_settings_t) * (idx_max - i));
1591 idx_max--;
1594 obj_list = talloc_realloc(NULL, obj_list, struct m_obj_settings,
1595 idx_max + 1);
1596 VAL(dst) = obj_list;
1598 return 1;
1601 static void free_obj_settings_list(void *dst)
1603 int n;
1604 m_obj_settings_t *d;
1606 if (!dst || !VAL(dst))
1607 return;
1609 d = VAL(dst);
1610 for (n = 0; d[n].name; n++) {
1611 talloc_free(d[n].name);
1612 free_str_list(&(d[n].attribs));
1614 talloc_free(d);
1615 VAL(dst) = NULL;
1618 static int parse_obj_settings_list(const m_option_t *opt, struct bstr name,
1619 struct bstr param, bool ambiguous_param,
1620 void *dst, void *talloc_ctx)
1622 int len = strlen(opt->name);
1623 m_obj_settings_t *res = NULL, *queue = NULL, *head = NULL;
1624 int op = OP_NONE;
1626 // We need the objects list
1627 if (!opt->priv)
1628 return M_OPT_INVALID;
1630 if (opt->name[len - 1] == '*' && (name.len > len - 1)) {
1631 struct bstr suffix = bstr_cut(name, len - 1);
1632 if (bstrcasecmp0(suffix, "-add") == 0)
1633 op = OP_ADD;
1634 else if (bstrcasecmp0(suffix, "-pre") == 0)
1635 op = OP_PRE;
1636 else if (bstrcasecmp0(suffix, "-del") == 0)
1637 op = OP_DEL;
1638 else if (bstrcasecmp0(suffix, "-clr") == 0)
1639 op = OP_CLR;
1640 else {
1641 char prefix[len];
1642 strncpy(prefix, opt->name, len - 1);
1643 prefix[len - 1] = '\0';
1644 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1645 "Option %.*s: unknown postfix %.*s\n"
1646 "Supported postfixes are:\n"
1647 " %s-add\n"
1648 " Append the given list to the current list\n\n"
1649 " %s-pre\n"
1650 " Prepend the given list to the current list\n\n"
1651 " %s-del x,y,...\n"
1652 " Remove the given elements. Take the list element index (starting from 0).\n"
1653 " Negative index can be used (i.e. -1 is the last element)\n\n"
1654 " %s-clr\n"
1655 " Clear the current list.\n",
1656 BSTR_P(name), BSTR_P(suffix), prefix, prefix, prefix, prefix);
1658 return M_OPT_UNKNOWN;
1662 // Clear the list ??
1663 if (op == OP_CLR) {
1664 if (dst)
1665 free_obj_settings_list(dst);
1666 return 0;
1669 if (param.len == 0)
1670 return M_OPT_MISSING_PARAM;
1672 switch (op) {
1673 case OP_ADD:
1674 if (dst)
1675 head = VAL(dst);
1676 break;
1677 case OP_PRE:
1678 if (dst)
1679 queue = VAL(dst);
1680 break;
1681 case OP_DEL:
1682 return obj_settings_list_del(name, param, false, dst);
1683 case OP_NONE:
1684 if (dst && VAL(dst))
1685 free_obj_settings_list(dst);
1686 break;
1687 default:
1688 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: FIXME\n", BSTR_P(name));
1689 return M_OPT_UNKNOWN;
1692 if (!bstrcmp0(param, "help")) {
1693 m_obj_list_t *ol = opt->priv;
1694 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available video filters:\n");
1695 for (int n = 0; ol->list[n]; n++)
1696 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-15s: %s\n",
1697 M_ST_MB(char *, ol->list[n], ol->name_off),
1698 M_ST_MB(char *, ol->list[n], ol->info_off));
1699 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1700 return M_OPT_EXIT - 1;
1703 struct bstr s = bstrdup(NULL, param);
1704 char *allocptr = s.start;
1705 int n = 0;
1706 while (s.len > 0) {
1707 struct bstr el = get_nextsep(&s, OPTION_LIST_SEPARATOR, 1);
1708 int r = parse_obj_settings(name, el, opt->priv, dst ? &res : NULL, n);
1709 if (r < 0) {
1710 talloc_free(allocptr);
1711 return r;
1713 s = bstr_cut(s, 1);
1714 n++;
1716 talloc_free(allocptr);
1717 if (n == 0)
1718 return M_OPT_INVALID;
1720 if (((opt->flags & M_OPT_MIN) && (n < opt->min)) ||
1721 ((opt->flags & M_OPT_MAX) && (n > opt->max)))
1722 return M_OPT_OUT_OF_RANGE;
1724 if (dst) {
1725 if (queue) {
1726 int qsize;
1727 for (qsize = 0; queue[qsize].name; qsize++)
1728 /* NOP */;
1729 res = talloc_realloc(NULL, res, struct m_obj_settings,
1730 qsize + n + 1);
1731 memcpy(&res[n], queue, (qsize + 1) * sizeof(m_obj_settings_t));
1732 n += qsize;
1733 talloc_free(queue);
1735 if (head) {
1736 int hsize;
1737 for (hsize = 0; head[hsize].name; hsize++)
1738 /* NOP */;
1739 head = talloc_realloc(NULL, head, struct m_obj_settings,
1740 hsize + n + 1);
1741 memcpy(&head[hsize], res, (n + 1) * sizeof(m_obj_settings_t));
1742 talloc_free(res);
1743 res = head;
1745 VAL(dst) = res;
1747 return 1;
1750 static void copy_obj_settings_list(const m_option_t *opt, void *dst,
1751 const void *src, void *talloc_ctx)
1753 m_obj_settings_t *d, *s;
1754 int n;
1756 if (!(dst && src))
1757 return;
1759 s = VAL(src);
1761 if (VAL(dst))
1762 free_obj_settings_list(dst);
1763 if (!s)
1764 return;
1768 for (n = 0; s[n].name; n++)
1769 /* NOP */;
1770 d = talloc_array(NULL, struct m_obj_settings, n + 1);
1771 for (n = 0; s[n].name; n++) {
1772 d[n].name = talloc_strdup(NULL, s[n].name);
1773 d[n].attribs = NULL;
1774 copy_str_list(NULL, &(d[n].attribs), &(s[n].attribs), NULL);
1776 d[n].name = NULL;
1777 d[n].attribs = NULL;
1778 VAL(dst) = d;
1781 const m_option_type_t m_option_type_obj_settings_list = {
1782 .name = "Object settings list",
1783 .size = sizeof(m_obj_settings_t *),
1784 .flags = M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
1785 .parse = parse_obj_settings_list,
1786 .copy = copy_obj_settings_list,
1787 .free = free_obj_settings_list,
1792 static int parse_obj_presets(const m_option_t *opt, struct bstr name,
1793 struct bstr param, bool ambiguous_param,
1794 void *dst, void *talloc_ctx)
1796 m_obj_presets_t *obj_p = (m_obj_presets_t *)opt->priv;
1797 const m_struct_t *in_desc;
1798 const m_struct_t *out_desc;
1799 int s, i;
1800 const unsigned char *pre;
1801 char *pre_name = NULL;
1803 if (!obj_p) {
1804 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: Presets need a "
1805 "pointer to a m_obj_presets_t in the priv field.\n",
1806 BSTR_P(name));
1807 return M_OPT_PARSER_ERR;
1810 if (param.len == 0)
1811 return M_OPT_MISSING_PARAM;
1813 pre = obj_p->presets;
1814 in_desc = obj_p->in_desc;
1815 out_desc = obj_p->out_desc ? obj_p->out_desc : obj_p->in_desc;
1816 s = in_desc->size;
1818 if (!bstrcmp0(param, "help")) {
1819 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available presets for %s->%.*s:",
1820 out_desc->name, BSTR_P(name));
1821 for (pre = obj_p->presets;
1822 (pre_name = M_ST_MB(char *, pre, obj_p->name_off)); pre += s)
1823 mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", pre_name);
1824 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
1825 return M_OPT_EXIT - 1;
1828 for (pre_name = M_ST_MB(char *, pre, obj_p->name_off); pre_name;
1829 pre += s, pre_name = M_ST_MB(char *, pre, obj_p->name_off))
1830 if (!bstrcmp0(param, pre_name))
1831 break;
1832 if (!pre_name) {
1833 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1834 "Option %.*s: There is no preset named %.*s\n"
1835 "Available presets are:", BSTR_P(name), BSTR_P(param));
1836 for (pre = obj_p->presets;
1837 (pre_name = M_ST_MB(char *, pre, obj_p->name_off)); pre += s)
1838 mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", pre_name);
1839 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
1840 return M_OPT_INVALID;
1843 if (!dst)
1844 return 1;
1846 for (i = 0; in_desc->fields[i].name; i++) {
1847 const m_option_t *out_opt = m_option_list_find(out_desc->fields,
1848 in_desc->fields[i].name);
1849 if (!out_opt) {
1850 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1851 "Option %.*s: Unable to find the target option for field %s.\n"
1852 "Please report this to the developers.\n",
1853 BSTR_P(name), in_desc->fields[i].name);
1854 return M_OPT_PARSER_ERR;
1856 m_option_copy(out_opt, M_ST_MB_P(dst, out_opt->p),
1857 M_ST_MB_P(pre, in_desc->fields[i].p));
1859 return 1;
1863 const m_option_type_t m_option_type_obj_presets = {
1864 .name = "Object presets",
1865 .parse = parse_obj_presets,
1868 static int parse_custom_url(const m_option_t *opt, struct bstr name,
1869 struct bstr url, bool ambiguous_param, void *dst,
1870 void *talloc_ctx)
1872 int r;
1873 m_struct_t *desc = opt->priv;
1875 if (!desc) {
1876 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: Custom URL needs "
1877 "a pointer to a m_struct_t in the priv field.\n", BSTR_P(name));
1878 return M_OPT_PARSER_ERR;
1881 // extract the protocol
1882 int idx = bstr_find0(url, "://");
1883 if (idx < 0) {
1884 // Filename only
1885 if (m_option_list_find(desc->fields, "filename")) {
1886 m_struct_set(desc, dst, "filename", url);
1887 return 1;
1889 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1890 "Option %.*s: URL doesn't have a valid protocol!\n",
1891 BSTR_P(name));
1892 return M_OPT_INVALID;
1894 struct bstr ptr1 = bstr_cut(url, idx + 3);
1895 if (m_option_list_find(desc->fields, "string")) {
1896 if (ptr1.len > 0) {
1897 m_struct_set(desc, dst, "string", ptr1);
1898 return 1;
1901 if (dst && m_option_list_find(desc->fields, "protocol")) {
1902 r = m_struct_set(desc, dst, "protocol", bstr_splice(url, 0, idx));
1903 if (r < 0) {
1904 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1905 "Option %.*s: Error while setting protocol.\n",
1906 BSTR_P(name));
1907 return r;
1911 // check if a username:password is given
1912 idx = bstrchr(ptr1, '/');
1913 if (idx < 0)
1914 idx = ptr1.len;
1915 struct bstr hostpart = bstr_splice(ptr1, 0, idx);
1916 struct bstr path = bstr_cut(ptr1, idx);
1917 idx = bstrchr(hostpart, '@');
1918 if (idx >= 0) {
1919 // We got something, at least a username...
1920 if (!m_option_list_find(desc->fields, "username")) {
1921 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
1922 "Option %.*s: This URL doesn't have a username part.\n",
1923 BSTR_P(name));
1924 // skip
1925 } else {
1926 struct bstr userpass = bstr_splice(hostpart, 0, idx);
1927 idx = bstrchr(userpass, ':');
1928 if (idx >= 0) {
1929 // We also have a password
1930 if (!m_option_list_find(desc->fields, "password")) {
1931 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
1932 "Option %.*s: This URL doesn't have a password part.\n",
1933 BSTR_P(name));
1934 // skip
1935 } else { // Username and password
1936 if (dst) {
1937 r = m_struct_set(desc, dst, "username",
1938 bstr_splice(userpass, 0, idx));
1939 if (r < 0) {
1940 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1941 "Option %.*s: Error while setting username.\n",
1942 BSTR_P(name));
1943 return r;
1945 r = m_struct_set(desc, dst, "password",
1946 bstr_splice(userpass, idx+1,
1947 userpass.len));
1948 if (r < 0) {
1949 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1950 "Option %.*s: Error while setting password.\n",
1951 BSTR_P(name));
1952 return r;
1956 } else { // User name only
1957 r = m_struct_set(desc, dst, "username", userpass);
1958 if (r < 0) {
1959 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1960 "Option %.*s: Error while setting username.\n",
1961 BSTR_P(name));
1962 return r;
1966 hostpart = bstr_cut(hostpart, idx + 1);
1969 // Before looking for a port number check if we have an IPv6 type
1970 // numeric address.
1971 // In an IPv6 URL the numeric address should be inside square braces.
1972 int idx1 = bstrchr(hostpart, '[');
1973 int idx2 = bstrchr(hostpart, ']');
1974 struct bstr portstr = hostpart;
1975 bool v6addr = false;
1976 if (idx1 >= 0 && idx2 >= 0 && idx1 < idx2) {
1977 // we have an IPv6 numeric address
1978 portstr = bstr_cut(hostpart, idx2);
1979 hostpart = bstr_splice(hostpart, idx1 + 1, idx2);
1980 v6addr = true;
1983 idx = bstrchr(portstr, ':');
1984 if (idx >= 0) {
1985 if (!v6addr)
1986 hostpart = bstr_splice(hostpart, 0, idx);
1987 // We have an URL beginning like http://www.hostname.com:1212
1988 // Get the port number
1989 if (!m_option_list_find(desc->fields, "port")) {
1990 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
1991 "Option %.*s: This URL doesn't have a port part.\n",
1992 BSTR_P(name));
1993 // skip
1994 } else {
1995 if (dst) {
1996 int p = bstrtoll(bstr_cut(portstr, idx + 1), NULL, 0);
1997 char tmp[100];
1998 snprintf(tmp, 99, "%d", p);
1999 r = m_struct_set(desc, dst, "port", bstr(tmp));
2000 if (r < 0) {
2001 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2002 "Option %.*s: Error while setting port.\n",
2003 BSTR_P(name));
2004 return r;
2009 // Get the hostname
2010 if (hostpart.len > 0) {
2011 if (!m_option_list_find(desc->fields, "hostname")) {
2012 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
2013 "Option %.*s: This URL doesn't have a hostname part.\n",
2014 BSTR_P(name));
2015 // skip
2016 } else {
2017 r = m_struct_set(desc, dst, "hostname", hostpart);
2018 if (r < 0) {
2019 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2020 "Option %.*s: Error while setting hostname.\n",
2021 BSTR_P(name));
2022 return r;
2026 // Look if a path is given
2027 if (path.len > 1) { // not just "/"
2028 // copy the path/filename in the URL container
2029 if (!m_option_list_find(desc->fields, "filename")) {
2030 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
2031 "Option %.*s: This URL doesn't have a filename part.\n",
2032 BSTR_P(name));
2033 // skip
2034 } else {
2035 if (dst) {
2036 char *fname = bstrdup0(NULL, bstr_cut(path, 1));
2037 url_unescape_string(fname, fname);
2038 r = m_struct_set(desc, dst, "filename", bstr(fname));
2039 talloc_free(fname);
2040 if (r < 0) {
2041 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2042 "Option %.*s: Error while setting filename.\n",
2043 BSTR_P(name));
2044 return r;
2049 return 1;
2052 /// TODO : Write the other needed funcs for 'normal' options
2053 const m_option_type_t m_option_type_custom_url = {
2054 .name = "Custom URL",
2055 .parse = parse_custom_url,