cleanup: Silence compilation warnings on MinGW-w64
[mplayer.git] / m_option.c
blobd5113bb33a1f2b0f963e07b9bed13df99bb3269d
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)
82 if (dst && src)
83 memcpy(dst, src, opt->type->size);
86 // Flag
88 #define VAL(x) (*(int *)(x))
90 static int parse_flag(const m_option_t *opt, struct bstr name,
91 struct bstr param, bool ambiguous_param, void *dst)
93 if (param.len && !ambiguous_param) {
94 char * const enable[] = { "yes", "on", "ja", "si", "igen", "y", "j",
95 "i", "tak", "ja", "true", "1" };
96 for (int i = 0; i < sizeof(enable) / sizeof(enable[0]); i++) {
97 if (!bstrcasecmp0(param, enable[i])) {
98 if (dst)
99 VAL(dst) = opt->max;
100 return 1;
103 char * const disable[] = { "no", "off", "nein", "nicht", "nem", "n",
104 "nie", "nej", "false", "0" };
105 for (int i = 0; i < sizeof(disable) / sizeof(disable[0]); i++) {
106 if (!bstrcasecmp0(param, disable[i])) {
107 if (dst)
108 VAL(dst) = opt->min;
109 return 1;
112 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
113 "Invalid parameter for %.*s flag: %.*s\n",
114 BSTR_P(name), BSTR_P(param));
115 return M_OPT_INVALID;
116 } else {
117 if (dst)
118 VAL(dst) = opt->max;
119 return 0;
123 static char *print_flag(const m_option_t *opt, const void *val)
125 if (VAL(val) == opt->min)
126 return talloc_strdup(NULL, "no");
127 else
128 return talloc_strdup(NULL, "yes");
131 const m_option_type_t m_option_type_flag = {
132 "Flag",
133 "need yes or no in config files",
134 sizeof(int),
136 parse_flag,
137 print_flag,
138 copy_opt,
139 copy_opt,
140 NULL,
141 NULL
144 // Integer
146 static int parse_longlong(const m_option_t *opt, struct bstr name,
147 struct bstr param, bool ambiguous_param, void *dst)
149 if (param.len == 0)
150 return M_OPT_MISSING_PARAM;
152 struct bstr rest;
153 long long tmp_int = bstrtoll(param, &rest, 10);
154 if (rest.len)
155 tmp_int = bstrtoll(param, &rest, 0);
156 if (rest.len) {
157 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
158 "The %.*s option must be an integer: %.*s\n",
159 BSTR_P(name), BSTR_P(param));
160 return M_OPT_INVALID;
163 if ((opt->flags & M_OPT_MIN) && (tmp_int < opt->min)) {
164 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
165 "The %.*s option must be >= %d: %.*s\n",
166 BSTR_P(name), (int) opt->min, BSTR_P(param));
167 return M_OPT_OUT_OF_RANGE;
170 if ((opt->flags & M_OPT_MAX) && (tmp_int > opt->max)) {
171 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
172 "The %.*s option must be <= %d: %.*s\n",
173 BSTR_P(name), (int) opt->max, BSTR_P(param));
174 return M_OPT_OUT_OF_RANGE;
177 if (dst)
178 *(long long *)dst = tmp_int;
180 return 1;
183 static int parse_int(const m_option_t *opt, struct bstr name,
184 struct bstr param, bool ambiguous_param, void *dst)
186 long long tmp;
187 int r = parse_longlong(opt, name, param, false, &tmp);
188 if (r >= 0 && dst)
189 *(int *)dst = tmp;
190 return r;
193 static int parse_int64(const m_option_t *opt, struct bstr name,
194 struct bstr param, bool ambiguous_param, void *dst)
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 "Integer",
214 sizeof(int),
216 parse_int,
217 print_int,
218 copy_opt,
219 copy_opt,
220 NULL,
221 NULL
224 const m_option_type_t m_option_type_int64 = {
225 "Integer64",
227 sizeof(int64_t),
229 parse_int64,
230 print_int,
231 copy_opt,
232 copy_opt,
233 NULL,
234 NULL
237 static int parse_intpair(const struct m_option *opt, struct bstr name,
238 struct bstr param, bool ambiguous_param, void *dst)
240 if (param.len == 0)
241 return M_OPT_MISSING_PARAM;
243 struct bstr s = param;
244 int end = -1;
245 int start = bstrtoll(s, &s, 10);
246 if (s.len == param.len)
247 goto bad;
248 if (s.len > 0) {
249 if (!bstr_startswith0(s, "-"))
250 goto bad;
251 s = bstr_cut(s, 1);
253 if (s.len > 0)
254 end = bstrtoll(s, &s, 10);
255 if (s.len > 0)
256 goto bad;
258 if (dst) {
259 int *p = dst;
260 p[0] = start;
261 p[1] = end;
264 return 1;
266 bad:
267 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid integer range "
268 "specification for option %.*s: %.*s\n",
269 BSTR_P(name), BSTR_P(param));
270 return M_OPT_INVALID;
273 const struct m_option_type m_option_type_intpair = {
274 .name = "Int[-Int]",
275 .size = sizeof(int[2]),
276 .parse = parse_intpair,
277 .save = copy_opt,
278 .set = copy_opt,
281 static int parse_choice(const struct m_option *opt, struct bstr name,
282 struct bstr param, bool ambiguous_param, void *dst)
284 if (param.len == 0)
285 return M_OPT_MISSING_PARAM;
287 struct m_opt_choice_alternatives *alt;
288 for (alt = opt->priv; alt->name; alt++)
289 if (!bstrcasecmp0(param, alt->name))
290 break;
291 if (!alt->name) {
292 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
293 "Invalid value for option %.*s: %.*s\n",
294 BSTR_P(name), BSTR_P(param));
295 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Valid values are:");
296 for (alt = opt->priv; alt->name; alt++)
297 mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", alt->name);
298 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
299 return M_OPT_INVALID;
301 if (dst)
302 *(int *)dst = alt->value;
304 return 1;
307 static char *print_choice(const m_option_t *opt, const void *val)
309 int v = *(int *)val;
310 struct m_opt_choice_alternatives *alt;
311 for (alt = opt->priv; alt->name; alt++)
312 if (alt->value == v)
313 return talloc_strdup(NULL, alt->name);
314 abort();
317 const struct m_option_type m_option_type_choice = {
318 .name = "String", // same as arbitrary strings in option list for now
319 .size = sizeof(int),
320 .parse = parse_choice,
321 .print = print_choice,
322 .save = copy_opt,
323 .set = copy_opt,
326 // Float
328 #undef VAL
329 #define VAL(x) (*(double *)(x))
331 static int parse_double(const m_option_t *opt, struct bstr name,
332 struct bstr param, bool ambiguous_param, void *dst)
334 if (param.len == 0)
335 return M_OPT_MISSING_PARAM;
337 struct bstr rest;
338 double tmp_float = bstrtod(param, &rest);
340 switch (rest.len ? rest.start[0] : 0) {
341 case ':':
342 case '/':
343 tmp_float /= bstrtod(bstr_cut(rest, 1), &rest);
344 break;
345 case '.':
346 case ',':
347 /* we also handle floats specified with
348 * non-locale decimal point ::atmos
350 rest = bstr_cut(rest, 1);
351 if (tmp_float < 0)
352 tmp_float -= 1.0 / pow(10, rest.len) * bstrtod(rest, &rest);
353 else
354 tmp_float += 1.0 / pow(10, rest.len) * bstrtod(rest, &rest);
355 break;
358 if (rest.len) {
359 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
360 "The %.*s option must be a floating point number or a "
361 "ratio (numerator[:/]denominator): %.*s\n",
362 BSTR_P(name), BSTR_P(param));
363 return M_OPT_INVALID;
366 if (opt->flags & M_OPT_MIN)
367 if (tmp_float < opt->min) {
368 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
369 "The %.*s option must be >= %f: %.*s\n",
370 BSTR_P(name), opt->min, BSTR_P(param));
371 return M_OPT_OUT_OF_RANGE;
374 if (opt->flags & M_OPT_MAX)
375 if (tmp_float > opt->max) {
376 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
377 "The %.*s option must be <= %f: %.*s\n",
378 BSTR_P(name), opt->max, BSTR_P(param));
379 return M_OPT_OUT_OF_RANGE;
382 if (dst)
383 VAL(dst) = tmp_float;
384 return 1;
387 static char *print_double(const m_option_t *opt, const void *val)
389 opt = NULL;
390 return talloc_asprintf(NULL, "%f", VAL(val));
393 const m_option_type_t m_option_type_double = {
394 "Double",
395 "double precision floating point number or ratio (numerator[:/]denominator)",
396 sizeof(double),
398 parse_double,
399 print_double,
400 copy_opt,
401 copy_opt,
402 NULL,
403 NULL
406 #undef VAL
407 #define VAL(x) (*(float *)(x))
409 static int parse_float(const m_option_t *opt, struct bstr name,
410 struct bstr param, bool ambiguous_param, void *dst)
412 double tmp;
413 int r = parse_double(opt, name, param, false, &tmp);
414 if (r == 1 && dst)
415 VAL(dst) = tmp;
416 return r;
419 static char *print_float(const m_option_t *opt, const void *val)
421 opt = NULL;
422 return talloc_asprintf(NULL, "%f", VAL(val));
425 const m_option_type_t m_option_type_float = {
426 "Float",
427 "floating point number or ratio (numerator[:/]denominator)",
428 sizeof(float),
430 parse_float,
431 print_float,
432 copy_opt,
433 copy_opt,
434 NULL,
435 NULL
438 ///////////// Position
439 #undef VAL
440 #define VAL(x) (*(off_t *)(x))
442 static int parse_position(const m_option_t *opt, struct bstr name,
443 struct bstr param, bool ambiguous_param, void *dst)
445 long long tmp;
446 int r = parse_longlong(opt, name, param, false, &tmp);
447 if (r >= 0 && dst)
448 *(off_t *)dst = tmp;
449 return r;
452 static char *print_position(const m_option_t *opt, const void *val)
454 return talloc_asprintf(NULL, "%"PRId64, (int64_t)VAL(val));
457 const m_option_type_t m_option_type_position = {
458 "Position",
459 "Integer (off_t)",
460 sizeof(off_t),
462 parse_position,
463 print_position,
464 copy_opt,
465 copy_opt,
466 NULL,
467 NULL
471 ///////////// String
473 #undef VAL
474 #define VAL(x) (*(char **)(x))
476 static int parse_str(const m_option_t *opt, struct bstr name,
477 struct bstr param, bool ambiguous_param, void *dst)
479 if ((opt->flags & M_OPT_MIN) && (param.len < opt->min)) {
480 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
481 "Parameter must be >= %d chars: %.*s\n",
482 (int) opt->min, BSTR_P(param));
483 return M_OPT_OUT_OF_RANGE;
486 if ((opt->flags & M_OPT_MAX) && (param.len > opt->max)) {
487 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
488 "Parameter must be <= %d chars: %.*s\n",
489 (int) opt->max, BSTR_P(param));
490 return M_OPT_OUT_OF_RANGE;
493 if (dst) {
494 talloc_free(VAL(dst));
495 VAL(dst) = bstrdup0(NULL, param);
498 return 1;
502 static char *print_str(const m_option_t *opt, const void *val)
504 return (val && VAL(val)) ? talloc_strdup(NULL, VAL(val)) : NULL;
507 static void copy_str(const m_option_t *opt, void *dst, const void *src)
509 if (dst && src) {
510 talloc_free(VAL(dst));
511 VAL(dst) = talloc_strdup(NULL, VAL(src));
515 static void free_str(void *src)
517 if (src && VAL(src)) {
518 talloc_free(VAL(src));
519 VAL(src) = NULL;
523 const m_option_type_t m_option_type_string = {
524 "String",
526 sizeof(char *),
527 M_OPT_TYPE_DYNAMIC,
528 parse_str,
529 print_str,
530 copy_str,
531 copy_str,
532 copy_str,
533 free_str
536 //////////// String list
538 #undef VAL
539 #define VAL(x) (*(char ***)(x))
541 #define OP_NONE 0
542 #define OP_ADD 1
543 #define OP_PRE 2
544 #define OP_DEL 3
545 #define OP_CLR 4
547 static void free_str_list(void *dst)
549 char **d;
550 int i;
552 if (!dst || !VAL(dst))
553 return;
554 d = VAL(dst);
556 for (i = 0; d[i] != NULL; i++)
557 talloc_free(d[i]);
558 talloc_free(d);
559 VAL(dst) = NULL;
562 static int str_list_add(char **add, int n, void *dst, int pre)
564 char **lst = VAL(dst);
565 int ln;
567 if (!dst)
568 return M_OPT_PARSER_ERR;
569 lst = VAL(dst);
571 for (ln = 0; lst && lst[ln]; ln++)
572 /**/;
574 lst = talloc_realloc(NULL, lst, char *, n + ln + 1);
576 if (pre) {
577 memmove(&lst[n], lst, ln * sizeof(char *));
578 memcpy(lst, add, n * sizeof(char *));
579 } else
580 memcpy(&lst[ln], add, n * sizeof(char *));
581 // (re-)add NULL-termination
582 lst[ln + n] = NULL;
584 talloc_free(add);
586 VAL(dst) = lst;
588 return 1;
591 static int str_list_del(char **del, int n, void *dst)
593 char **lst, *ep;
594 int i, ln, s;
595 long idx;
597 if (!dst)
598 return M_OPT_PARSER_ERR;
599 lst = VAL(dst);
601 for (ln = 0; lst && lst[ln]; ln++)
602 /**/;
603 s = ln;
605 for (i = 0; del[i] != NULL; i++) {
606 idx = strtol(del[i], &ep, 0);
607 if (*ep) {
608 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid index: %s\n", del[i]);
609 talloc_free(del[i]);
610 continue;
612 talloc_free(del[i]);
613 if (idx < 0 || idx >= ln) {
614 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
615 "Index %ld is out of range.\n", idx);
616 continue;
617 } else if (!lst[idx])
618 continue;
619 talloc_free(lst[idx]);
620 lst[idx] = NULL;
621 s--;
623 talloc_free(del);
625 if (s == 0) {
626 talloc_free(lst);
627 VAL(dst) = NULL;
628 return 1;
631 // Don't bother shrinking the list allocation
632 for (i = 0, n = 0; i < ln; i++) {
633 if (!lst[i])
634 continue;
635 lst[n] = lst[i];
636 n++;
638 lst[s] = NULL;
640 return 1;
643 static struct bstr get_nextsep(struct bstr *ptr, char sep, bool modify)
645 struct bstr str = *ptr;
646 struct bstr orig = str;
647 for (;;) {
648 int idx = bstrchr(str, sep);
649 if (idx > 0 && str.start[idx - 1] == '\\') {
650 if (modify) {
651 memmove(str.start + idx - 1, str.start + idx, str.len - idx);
652 str.len--;
653 str = bstr_cut(str, idx);
654 } else
655 str = bstr_cut(str, idx + 1);
656 } else {
657 str = bstr_cut(str, idx < 0 ? str.len : idx);
658 break;
661 *ptr = str;
662 return bstr_splice(orig, 0, str.start - orig.start);
665 static int parse_str_list(const m_option_t *opt, struct bstr name,
666 struct bstr param, bool ambiguous_param, void *dst)
668 char **res;
669 int op = OP_NONE;
670 int len = strlen(opt->name);
671 if (opt->name[len - 1] == '*' && (name.len > len - 1)) {
672 struct bstr suffix = bstr_cut(name, len - 1);
673 if (bstrcasecmp0(suffix, "-add") == 0)
674 op = OP_ADD;
675 else if (bstrcasecmp0(suffix, "-pre") == 0)
676 op = OP_PRE;
677 else if (bstrcasecmp0(suffix, "-del") == 0)
678 op = OP_DEL;
679 else if (bstrcasecmp0(suffix, "-clr") == 0)
680 op = OP_CLR;
681 else
682 return M_OPT_UNKNOWN;
685 // Clear the list ??
686 if (op == OP_CLR) {
687 if (dst)
688 free_str_list(dst);
689 return 0;
692 // All other ops need a param
693 if (param.len == 0)
694 return M_OPT_MISSING_PARAM;
696 // custom type for "profile" calls this but uses ->priv for something else
697 char separator = opt->type == &m_option_type_string_list && opt->priv ?
698 *(char *)opt->priv : OPTION_LIST_SEPARATOR;
699 int n = 0;
700 struct bstr str = param;
701 while (str.len) {
702 get_nextsep(&str, separator, 0);
703 str = bstr_cut(str, 1);
704 n++;
706 if (n == 0)
707 return M_OPT_INVALID;
708 if (((opt->flags & M_OPT_MIN) && (n < opt->min)) ||
709 ((opt->flags & M_OPT_MAX) && (n > opt->max)))
710 return M_OPT_OUT_OF_RANGE;
712 if (!dst)
713 return 1;
715 res = talloc_array(NULL, char *, n + 2);
716 str = bstrdup(NULL, param);
717 char *ptr = str.start;
718 n = 0;
720 while (1) {
721 struct bstr el = get_nextsep(&str, separator, 1);
722 res[n] = bstrdup0(NULL, el);
723 n++;
724 if (!str.len)
725 break;
726 str = bstr_cut(str, 1);
728 res[n] = NULL;
729 talloc_free(ptr);
731 switch (op) {
732 case OP_ADD:
733 return str_list_add(res, n, dst, 0);
734 case OP_PRE:
735 return str_list_add(res, n, dst, 1);
736 case OP_DEL:
737 return str_list_del(res, n, dst);
740 if (VAL(dst))
741 free_str_list(dst);
742 VAL(dst) = res;
744 return 1;
747 static void copy_str_list(const m_option_t *opt, void *dst, const void *src)
749 int n;
750 char **d, **s;
752 if (!(dst && src))
753 return;
754 s = VAL(src);
756 if (VAL(dst))
757 free_str_list(dst);
759 if (!s) {
760 VAL(dst) = NULL;
761 return;
764 for (n = 0; s[n] != NULL; n++)
765 /* NOTHING */;
766 d = talloc_array(NULL, char *, n + 1);
767 for (; n >= 0; n--)
768 d[n] = talloc_strdup(NULL, s[n]);
770 VAL(dst) = d;
773 static char *print_str_list(const m_option_t *opt, const void *src)
775 char **lst = NULL;
776 char *ret = NULL;
778 if (!(src && VAL(src)))
779 return NULL;
780 lst = VAL(src);
782 for (int i = 0; lst[i]; i++) {
783 if (ret)
784 ret = talloc_strdup_append_buffer(ret, ",");
785 ret = talloc_strdup_append_buffer(ret, lst[i]);
787 return ret;
790 const m_option_type_t m_option_type_string_list = {
791 "String list",
792 "A list of strings separated by ','\n"
793 "Option with a name ending in an * permits using the following suffix: \n"
794 "\t-add: Add the given parameters at the end of the list.\n"
795 "\t-pre: Add the given parameters at the beginning of the list.\n"
796 "\t-del: Remove the entry at the given indices.\n"
797 "\t-clr: Clear the list.\n"
798 "e.g: -vf-add flip,mirror -vf-del 2,5\n",
799 sizeof(char **),
800 M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
801 parse_str_list,
802 print_str_list,
803 copy_str_list,
804 copy_str_list,
805 copy_str_list,
806 free_str_list
810 /////////////////// Func based options
812 // A chained list to save the various calls for func_param
813 struct m_func_save {
814 struct m_func_save *next;
815 char *name;
816 char *param;
819 #undef VAL
820 #define VAL(x) (*(struct m_func_save **)(x))
822 static void free_func_pf(void *src)
824 struct m_func_save *s, *n;
826 if (!src)
827 return;
829 s = VAL(src);
831 while (s) {
832 n = s->next;
833 talloc_free(s->name);
834 talloc_free(s->param);
835 talloc_free(s);
836 s = n;
838 VAL(src) = NULL;
841 // Parser for func_param
842 static int parse_func_pf(const m_option_t *opt, struct bstr name,
843 struct bstr param, bool ambiguous_param, void *dst)
845 struct m_func_save *s, *p;
847 if (!dst)
848 return 1;
850 s = talloc_zero(NULL, struct m_func_save);
851 s->name = bstrdup0(NULL, name);
852 s->param = bstrdup0(NULL, param);
854 p = VAL(dst);
855 if (p) {
856 for (; p->next != NULL; p = p->next)
857 /**/;
858 p->next = s;
859 } else
860 VAL(dst) = s;
862 return 1;
865 static void copy_func_pf(const m_option_t *opt, void *dst, const void *src)
867 struct m_func_save *d = NULL, *s, *last = NULL;
869 if (!(dst && src))
870 return;
871 s = VAL(src);
873 if (VAL(dst))
874 free_func_pf(dst);
876 while (s) {
877 d = talloc_zero(NULL, struct m_func_save);
878 d->name = talloc_strdup(NULL, s->name);
879 d->param = talloc_strdup(NULL, s->param);
880 if (last)
881 last->next = d;
882 else
883 VAL(dst) = d;
884 last = d;
885 s = s->next;
891 /////////////////// Func_param
893 static void set_func_param(const m_option_t *opt, void *dst, const void *src)
895 struct m_func_save *s;
897 if (!src)
898 return;
899 s = VAL(src);
901 if (!s)
902 return;
904 for (; s != NULL; s = s->next)
905 ((m_opt_func_param_t) opt->p)(opt, s->param);
908 const m_option_type_t m_option_type_func_param = {
909 "Func param",
911 sizeof(struct m_func_save *),
912 M_OPT_TYPE_INDIRECT,
913 parse_func_pf,
914 NULL,
915 NULL, // Nothing to do on save
916 set_func_param,
917 copy_func_pf,
918 free_func_pf
921 /////////////// Func
923 #undef VAL
925 static int parse_func(const m_option_t *opt, struct bstr name,
926 struct bstr param, bool ambiguous_param, void *dst)
928 return 0;
931 static void set_func(const m_option_t *opt, void *dst, const void *src)
933 ((m_opt_func_t) opt->p)(opt);
936 const m_option_type_t m_option_type_func = {
937 "Func",
939 sizeof(int),
940 M_OPT_TYPE_INDIRECT,
941 parse_func,
942 NULL,
943 NULL, // Nothing to do on save
944 set_func,
945 NULL,
946 NULL
949 /////////////////// Print
951 static int parse_print(const m_option_t *opt, struct bstr name,
952 struct bstr param, bool ambiguous_param, void *dst)
954 if (opt->type == CONF_TYPE_PRINT_INDIRECT)
955 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s", *(char **) opt->p);
956 else if (opt->type == CONF_TYPE_PRINT_FUNC) {
957 char *name0 = bstrdup0(NULL, name);
958 char *param0 = bstrdup0(NULL, param);
959 int r = ((m_opt_func_full_t) opt->p)(opt, name0, param0);
960 talloc_free(name0);
961 talloc_free(param0);
962 return r;
963 } else
964 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s", mp_gtext(opt->p));
966 if (opt->priv == NULL)
967 return M_OPT_EXIT;
968 return 0;
971 const m_option_type_t m_option_type_print = {
972 "Print",
976 parse_print,
977 NULL,
978 NULL,
979 NULL,
980 NULL,
981 NULL
984 const m_option_type_t m_option_type_print_indirect = {
985 "Print",
989 parse_print,
990 NULL,
991 NULL,
992 NULL,
993 NULL,
994 NULL
997 const m_option_type_t m_option_type_print_func = {
998 "Print",
1001 M_OPT_TYPE_ALLOW_WILDCARD,
1002 parse_print,
1003 NULL,
1004 NULL,
1005 NULL,
1006 NULL,
1007 NULL
1011 /////////////////////// Subconfig
1012 #undef VAL
1013 #define VAL(x) (*(char ***)(x))
1015 static int parse_subconf(const m_option_t *opt, struct bstr name,
1016 struct bstr param, bool ambiguous_param, void *dst)
1018 int nr = 0, i;
1019 char **lst = NULL;
1021 if (param.len == 0)
1022 return M_OPT_MISSING_PARAM;
1024 struct bstr p = param;
1025 const struct m_option *subopts = opt->p;
1027 while (p.len) {
1028 int optlen = bstrcspn(p, ":=");
1029 struct bstr subopt = bstr_splice(p, 0, optlen);
1030 struct bstr subparam = bstr(NULL);
1031 p = bstr_cut(p, optlen);
1032 if (bstr_startswith0(p, "=")) {
1033 p = bstr_cut(p, 1);
1034 if (bstr_startswith0(p, "\"")) {
1035 p = bstr_cut(p, 1);
1036 optlen = bstrcspn(p, "\"");
1037 subparam = bstr_splice(p, 0, optlen);
1038 p = bstr_cut(p, optlen);
1039 if (!bstr_startswith0(p, "\"")) {
1040 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1041 "Terminating '\"' missing for '%.*s'\n",
1042 BSTR_P(subopt));
1043 return M_OPT_INVALID;
1045 p = bstr_cut(p, 1);
1046 } else if (bstr_startswith0(p, "%")) {
1047 p = bstr_cut(p, 1);
1048 optlen = bstrtoll(p, &p, 0);
1049 if (!bstr_startswith0(p, "%") || (optlen > p.len - 1)) {
1050 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1051 "Invalid length %d for '%.*s'\n",
1052 optlen, BSTR_P(subopt));
1053 return M_OPT_INVALID;
1055 subparam = bstr_splice(p, 1, optlen + 1);
1056 p = bstr_cut(p, optlen + 1);
1057 } else {
1058 optlen = bstrcspn(p, ":");
1059 subparam = bstr_splice(p, 0, optlen);
1060 p = bstr_cut(p, optlen);
1063 if (bstr_startswith0(p, ":"))
1064 p = bstr_cut(p, 1);
1065 else if (p.len > 0) {
1066 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1067 "Incorrect termination for '%.*s'\n", BSTR_P(subopt));
1068 return M_OPT_INVALID;
1071 for (i = 0; subopts[i].name; i++)
1072 if (!bstrcmp0(subopt, subopts[i].name))
1073 break;
1074 if (!subopts[i].name) {
1075 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1076 "Option %.*s: Unknown suboption %.*s\n",
1077 BSTR_P(name), BSTR_P(subopt));
1078 return M_OPT_UNKNOWN;
1080 int r = m_option_parse(&subopts[i], subopt, subparam, false, NULL);
1081 if (r < 0)
1082 return r;
1083 if (dst) {
1084 lst = talloc_realloc(NULL, lst, char *, 2 * (nr + 2));
1085 lst[2 * nr] = bstrdup0(NULL, subopt);
1086 lst[2 * nr + 1] = subparam.len == 0 ? NULL :
1087 bstrdup0(NULL, subparam);
1088 memset(&lst[2 * (nr + 1)], 0, 2 * sizeof(char *));
1089 nr++;
1093 if (dst)
1094 VAL(dst) = lst;
1096 return 1;
1099 const m_option_type_t m_option_type_subconfig = {
1100 "Subconfig",
1101 "The syntax is -option opt1=foo:flag:opt2=blah",
1102 sizeof(int),
1103 M_OPT_TYPE_HAS_CHILD,
1104 parse_subconf,
1105 NULL,
1106 NULL,
1107 NULL,
1108 NULL,
1109 NULL
1112 #include "libmpcodecs/img_format.h"
1114 /* FIXME: snyc with img_format.h */
1115 static struct {
1116 const char *name;
1117 unsigned int fmt;
1118 } mp_imgfmt_list[] = {
1119 {"444p16le", IMGFMT_444P16_LE},
1120 {"444p16be", IMGFMT_444P16_BE},
1121 {"444p10le", IMGFMT_444P10_LE},
1122 {"444p10be", IMGFMT_444P10_BE},
1123 {"444p9le", IMGFMT_444P9_LE},
1124 {"444p9be", IMGFMT_444P9_BE},
1125 {"422p16le", IMGFMT_422P16_LE},
1126 {"422p16be", IMGFMT_422P16_BE},
1127 {"422p10le", IMGFMT_422P10_LE},
1128 {"422p10be", IMGFMT_422P10_BE},
1129 {"420p16le", IMGFMT_420P16_LE},
1130 {"420p16be", IMGFMT_420P16_BE},
1131 {"420p10le", IMGFMT_420P10_LE},
1132 {"420p10be", IMGFMT_420P10_BE},
1133 {"420p9le", IMGFMT_420P9_LE},
1134 {"420p9be", IMGFMT_420P9_BE},
1135 {"444p16", IMGFMT_444P16},
1136 {"444p10", IMGFMT_444P10},
1137 {"444p9", IMGFMT_444P9},
1138 {"422p16", IMGFMT_422P16},
1139 {"422p10", IMGFMT_422P10},
1140 {"420p10", IMGFMT_420P10},
1141 {"420p9", IMGFMT_420P9},
1142 {"420p16", IMGFMT_420P16},
1143 {"420a", IMGFMT_420A},
1144 {"444p", IMGFMT_444P},
1145 {"422p", IMGFMT_422P},
1146 {"411p", IMGFMT_411P},
1147 {"440p", IMGFMT_440P},
1148 {"yuy2", IMGFMT_YUY2},
1149 {"yvyu", IMGFMT_YVYU},
1150 {"uyvy", IMGFMT_UYVY},
1151 {"yvu9", IMGFMT_YVU9},
1152 {"if09", IMGFMT_IF09},
1153 {"yv12", IMGFMT_YV12},
1154 {"i420", IMGFMT_I420},
1155 {"iyuv", IMGFMT_IYUV},
1156 {"clpl", IMGFMT_CLPL},
1157 {"hm12", IMGFMT_HM12},
1158 {"y800", IMGFMT_Y800},
1159 {"y8", IMGFMT_Y8},
1160 {"nv12", IMGFMT_NV12},
1161 {"nv21", IMGFMT_NV21},
1162 {"bgr24", IMGFMT_BGR24},
1163 {"bgr32", IMGFMT_BGR32},
1164 {"bgr16", IMGFMT_BGR16},
1165 {"bgr15", IMGFMT_BGR15},
1166 {"bgr12", IMGFMT_BGR12},
1167 {"bgr8", IMGFMT_BGR8},
1168 {"bgr4", IMGFMT_BGR4},
1169 {"bg4b", IMGFMT_BG4B},
1170 {"bgr1", IMGFMT_BGR1},
1171 {"rgb48be", IMGFMT_RGB48BE},
1172 {"rgb48le", IMGFMT_RGB48LE},
1173 {"rgb48ne", IMGFMT_RGB48NE},
1174 {"rgb24", IMGFMT_RGB24},
1175 {"rgb32", IMGFMT_RGB32},
1176 {"rgb16", IMGFMT_RGB16},
1177 {"rgb15", IMGFMT_RGB15},
1178 {"rgb12", IMGFMT_RGB12},
1179 {"rgb8", IMGFMT_RGB8},
1180 {"rgb4", IMGFMT_RGB4},
1181 {"rg4b", IMGFMT_RG4B},
1182 {"rgb1", IMGFMT_RGB1},
1183 {"rgba", IMGFMT_RGBA},
1184 {"argb", IMGFMT_ARGB},
1185 {"bgra", IMGFMT_BGRA},
1186 {"abgr", IMGFMT_ABGR},
1187 {"mjpeg", IMGFMT_MJPEG},
1188 {"mjpg", IMGFMT_MJPEG},
1189 { NULL, 0 }
1192 static int parse_imgfmt(const m_option_t *opt, struct bstr name,
1193 struct bstr param, bool ambiguous_param, void *dst)
1195 uint32_t fmt = 0;
1196 int i;
1198 if (param.len == 0)
1199 return M_OPT_MISSING_PARAM;
1201 if (!bstrcmp0(param, "help")) {
1202 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available formats:");
1203 for (i = 0; mp_imgfmt_list[i].name; i++)
1204 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %s", mp_imgfmt_list[i].name);
1205 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1206 return M_OPT_EXIT - 1;
1209 if (bstr_startswith0(param, "0x"))
1210 fmt = bstrtoll(param, NULL, 16);
1211 else {
1212 for (i = 0; mp_imgfmt_list[i].name; i++) {
1213 if (!bstrcasecmp0(param, mp_imgfmt_list[i].name)) {
1214 fmt = mp_imgfmt_list[i].fmt;
1215 break;
1218 if (!mp_imgfmt_list[i].name) {
1219 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1220 "Option %.*s: unknown format name: '%.*s'\n",
1221 BSTR_P(name), BSTR_P(param));
1222 return M_OPT_INVALID;
1226 if (dst)
1227 *((uint32_t *)dst) = fmt;
1229 return 1;
1232 const m_option_type_t m_option_type_imgfmt = {
1233 "Image format",
1234 "Please report any missing colorspaces.",
1235 sizeof(uint32_t),
1237 parse_imgfmt,
1238 NULL,
1239 copy_opt,
1240 copy_opt,
1241 NULL,
1242 NULL
1245 #include "libaf/af_format.h"
1247 /* FIXME: snyc with af_format.h */
1248 static struct {
1249 const char *name;
1250 unsigned int fmt;
1251 } mp_afmt_list[] = {
1252 // SPECIAL
1253 {"mulaw", AF_FORMAT_MU_LAW},
1254 {"alaw", AF_FORMAT_A_LAW},
1255 {"mpeg2", AF_FORMAT_MPEG2},
1256 {"ac3le", AF_FORMAT_AC3_LE},
1257 {"ac3be", AF_FORMAT_AC3_BE},
1258 {"ac3ne", AF_FORMAT_AC3_NE},
1259 {"imaadpcm", AF_FORMAT_IMA_ADPCM},
1260 // ORDINARY
1261 {"u8", AF_FORMAT_U8},
1262 {"s8", AF_FORMAT_S8},
1263 {"u16le", AF_FORMAT_U16_LE},
1264 {"u16be", AF_FORMAT_U16_BE},
1265 {"u16ne", AF_FORMAT_U16_NE},
1266 {"s16le", AF_FORMAT_S16_LE},
1267 {"s16be", AF_FORMAT_S16_BE},
1268 {"s16ne", AF_FORMAT_S16_NE},
1269 {"u24le", AF_FORMAT_U24_LE},
1270 {"u24be", AF_FORMAT_U24_BE},
1271 {"u24ne", AF_FORMAT_U24_NE},
1272 {"s24le", AF_FORMAT_S24_LE},
1273 {"s24be", AF_FORMAT_S24_BE},
1274 {"s24ne", AF_FORMAT_S24_NE},
1275 {"u32le", AF_FORMAT_U32_LE},
1276 {"u32be", AF_FORMAT_U32_BE},
1277 {"u32ne", AF_FORMAT_U32_NE},
1278 {"s32le", AF_FORMAT_S32_LE},
1279 {"s32be", AF_FORMAT_S32_BE},
1280 {"s32ne", AF_FORMAT_S32_NE},
1281 {"floatle", AF_FORMAT_FLOAT_LE},
1282 {"floatbe", AF_FORMAT_FLOAT_BE},
1283 {"floatne", AF_FORMAT_FLOAT_NE},
1284 { NULL, 0 }
1287 static int parse_afmt(const m_option_t *opt, struct bstr name,
1288 struct bstr param, bool ambiguous_param, void *dst)
1290 uint32_t fmt = 0;
1291 int i;
1293 if (param.len == 0)
1294 return M_OPT_MISSING_PARAM;
1296 if (!bstrcmp0(param, "help")) {
1297 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available formats:");
1298 for (i = 0; mp_afmt_list[i].name; i++)
1299 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %s", mp_afmt_list[i].name);
1300 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1301 return M_OPT_EXIT - 1;
1304 if (bstr_startswith0(param, "0x"))
1305 fmt = bstrtoll(param, NULL, 16);
1306 else {
1307 for (i = 0; mp_afmt_list[i].name; i++) {
1308 if (!bstrcasecmp0(param, mp_afmt_list[i].name)) {
1309 fmt = mp_afmt_list[i].fmt;
1310 break;
1313 if (!mp_afmt_list[i].name) {
1314 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1315 "Option %.*s: unknown format name: '%.*s'\n",
1316 BSTR_P(name), BSTR_P(param));
1317 return M_OPT_INVALID;
1321 if (dst)
1322 *((uint32_t *)dst) = fmt;
1324 return 1;
1327 const m_option_type_t m_option_type_afmt = {
1328 "Audio format",
1329 "Please report any missing formats.",
1330 sizeof(uint32_t),
1332 parse_afmt,
1333 NULL,
1334 copy_opt,
1335 copy_opt,
1336 NULL,
1337 NULL
1341 static int parse_timestring(struct bstr str, double *time, char endchar)
1343 int a, b, len;
1344 double d;
1345 *time = 0; /* ensure initialization for error cases */
1346 if (bstr_sscanf(str, "%d:%d:%lf%n", &a, &b, &d, &len) >= 3)
1347 *time = 3600 * a + 60 * b + d;
1348 else if (bstr_sscanf(str, "%d:%lf%n", &a, &d, &len) >= 2)
1349 *time = 60 * a + d;
1350 else if (bstr_sscanf(str, "%lf%n", &d, &len) >= 1)
1351 *time = d;
1352 else
1353 return 0; /* unsupported time format */
1354 if (len < str.len && str.start[len] != endchar)
1355 return 0; /* invalid extra characters at the end */
1356 return len;
1360 static int parse_time(const m_option_t *opt, struct bstr name,
1361 struct bstr param, bool ambiguous_param, void *dst)
1363 double time;
1365 if (param.len == 0)
1366 return M_OPT_MISSING_PARAM;
1368 if (!parse_timestring(param, &time, 0)) {
1369 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: invalid time: '%.*s'\n",
1370 BSTR_P(name), BSTR_P(param));
1371 return M_OPT_INVALID;
1374 if (dst)
1375 *(double *)dst = time;
1376 return 1;
1379 const m_option_type_t m_option_type_time = {
1380 "Time",
1382 sizeof(double),
1384 parse_time,
1385 print_double,
1386 copy_opt,
1387 copy_opt,
1388 NULL,
1389 NULL
1393 // Time or size (-endpos)
1395 static int parse_time_size(const m_option_t *opt, struct bstr name,
1396 struct bstr param, bool ambiguous_param, void *dst)
1398 m_time_size_t ts;
1399 char unit[4];
1400 double end_at;
1402 if (param.len == 0)
1403 return M_OPT_MISSING_PARAM;
1405 ts.pos = 0;
1406 /* End at size parsing */
1407 if (bstr_sscanf(param, "%lf%3s", &end_at, unit) == 2) {
1408 ts.type = END_AT_SIZE;
1409 if (!strcasecmp(unit, "b"))
1411 else if (!strcasecmp(unit, "kb"))
1412 end_at *= 1024;
1413 else if (!strcasecmp(unit, "mb"))
1414 end_at *= 1024 * 1024;
1415 else if (!strcasecmp(unit, "gb"))
1416 end_at *= 1024 * 1024 * 1024;
1417 else
1418 ts.type = END_AT_NONE;
1420 if (ts.type == END_AT_SIZE) {
1421 ts.pos = end_at;
1422 goto out;
1426 /* End at time parsing. This has to be last because the parsing accepts
1427 * even a number followed by garbage */
1428 if (!parse_timestring(param, &end_at, 0)) {
1429 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1430 "Option %.*s: invalid time or size: '%.*s'\n",
1431 BSTR_P(name), BSTR_P(param));
1432 return M_OPT_INVALID;
1435 ts.type = END_AT_TIME;
1436 ts.pos = end_at;
1437 out:
1438 if (dst)
1439 *(m_time_size_t *)dst = ts;
1440 return 1;
1443 const m_option_type_t m_option_type_time_size = {
1444 "Time or size",
1446 sizeof(m_time_size_t),
1448 parse_time_size,
1449 NULL,
1450 copy_opt,
1451 copy_opt,
1452 NULL,
1453 NULL
1457 //// Objects (i.e. filters, etc) settings
1459 #include "m_struct.h"
1461 #undef VAL
1462 #define VAL(x) (*(m_obj_settings_t **)(x))
1464 static int find_obj_desc(struct bstr name, const m_obj_list_t *l,
1465 const m_struct_t **ret)
1467 int i;
1468 char *n;
1470 for (i = 0; l->list[i]; i++) {
1471 n = M_ST_MB(char *, l->list[i], l->name_off);
1472 if (!bstrcmp0(name, n)) {
1473 *ret = M_ST_MB(m_struct_t *, l->list[i], l->desc_off);
1474 return 1;
1477 return 0;
1480 static int get_obj_param(struct bstr opt_name, struct bstr obj_name,
1481 const m_struct_t *desc, struct bstr str, int *nold,
1482 int oldmax, char **dst)
1484 const m_option_t *opt;
1485 int r;
1487 int eq = bstrchr(str, '=');
1489 if (eq > 0) { // eq == 0 ignored
1490 struct bstr p = bstr_cut(str, eq + 1);
1491 str = bstr_splice(str, 0, eq);
1492 opt = m_option_list_findb(desc->fields, str);
1493 if (!opt) {
1494 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1495 "Option %.*s: %.*s doesn't have a %.*s parameter.\n",
1496 BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(str));
1497 return M_OPT_UNKNOWN;
1499 r = m_option_parse(opt, str, p, false, NULL);
1500 if (r < 0) {
1501 if (r > M_OPT_EXIT)
1502 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: "
1503 "Error while parsing %.*s parameter %.*s (%.*s)\n",
1504 BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(str),
1505 BSTR_P(p));
1506 return r;
1508 if (dst) {
1509 dst[0] = bstrdup0(NULL, str);
1510 dst[1] = bstrdup0(NULL, p);
1512 } else {
1513 if ((*nold) >= oldmax) {
1514 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: %.*s has only %d params, so you can't give more than %d unnamed params.\n",
1515 BSTR_P(opt_name), BSTR_P(obj_name), oldmax, oldmax);
1516 return M_OPT_OUT_OF_RANGE;
1518 opt = &desc->fields[(*nold)];
1519 r = m_option_parse(opt, bstr(opt->name), str, false, NULL);
1520 if (r < 0) {
1521 if (r > M_OPT_EXIT)
1522 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: "
1523 "Error while parsing %.*s parameter %s (%.*s)\n",
1524 BSTR_P(opt_name), BSTR_P(obj_name), opt->name,
1525 BSTR_P(str));
1526 return r;
1528 if (dst) {
1529 dst[0] = talloc_strdup(NULL, opt->name);
1530 dst[1] = bstrdup0(NULL, str);
1532 (*nold)++;
1534 return 1;
1537 static int get_obj_params(struct bstr opt_name, struct bstr name,
1538 struct bstr params, const m_struct_t *desc,
1539 char separator, char ***_ret)
1541 int n = 0, nold = 0, nopts;
1542 char **ret;
1544 if (!bstrcmp0(params, "help")) { // Help
1545 char min[50], max[50];
1546 if (!desc->fields) {
1547 mp_msg(MSGT_CFGPARSER, MSGL_INFO,
1548 "%.*s doesn't have any options.\n\n", BSTR_P(name));
1549 return M_OPT_EXIT - 1;
1551 mp_msg(MSGT_CFGPARSER, MSGL_INFO,
1552 "\n Name Type Min Max\n\n");
1553 for (n = 0; desc->fields[n].name; n++) {
1554 const m_option_t *opt = &desc->fields[n];
1555 if (opt->type->flags & M_OPT_TYPE_HAS_CHILD)
1556 continue;
1557 if (opt->flags & M_OPT_MIN)
1558 sprintf(min, "%-8.0f", opt->min);
1559 else
1560 strcpy(min, "No");
1561 if (opt->flags & M_OPT_MAX)
1562 sprintf(max, "%-8.0f", opt->max);
1563 else
1564 strcpy(max, "No");
1565 mp_msg(MSGT_CFGPARSER, MSGL_INFO,
1566 " %-20.20s %-15.15s %-10.10s %-10.10s\n",
1567 opt->name, opt->type->name, min, max);
1569 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1570 return M_OPT_EXIT - 1;
1573 for (nopts = 0; desc->fields[nopts].name; nopts++)
1574 /* NOP */;
1576 // TODO : Check that each opt can be parsed
1577 struct bstr s = params;
1578 while (1) {
1579 bool end = false;
1580 int idx = bstrchr(s, separator);
1581 if (idx < 0) {
1582 idx = s.len;
1583 end = true;
1585 struct bstr field = bstr_splice(s, 0, idx);
1586 s = bstr_cut(s, idx + 1);
1587 if (field.len == 0) { // Empty field, count it and go on
1588 nold++;
1589 } else {
1590 int r = get_obj_param(opt_name, name, desc, field, &nold, nopts,
1591 NULL);
1592 if (r < 0)
1593 return r;
1594 n++;
1596 if (end)
1597 break;
1599 if (nold > nopts) {
1600 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Too many options for %.*s\n",
1601 BSTR_P(name));
1602 return M_OPT_OUT_OF_RANGE;
1604 if (!_ret) // Just test
1605 return 1;
1606 if (n == 0) // No options or only empty options
1607 return 1;
1609 ret = talloc_array(NULL, char *, (n + 2) * 2);
1610 n = nold = 0;
1611 s = params;
1613 while (s.len > 0) {
1614 int idx = bstrchr(s, separator);
1615 if (idx < 0)
1616 idx = s.len;
1617 struct bstr field = bstr_splice(s, 0, idx);
1618 s = bstr_cut(s, idx + 1);
1619 if (field.len == 0) { // Empty field, count it and go on
1620 nold++;
1621 } else {
1622 get_obj_param(opt_name, name, desc, field, &nold, nopts,
1623 &ret[n * 2]);
1624 n++;
1627 ret[n * 2] = ret[n * 2 + 1] = NULL;
1628 *_ret = ret;
1630 return 1;
1633 static int parse_obj_params(const m_option_t *opt, struct bstr name,
1634 struct bstr param, bool ambiguous_param, void *dst)
1636 char **opts;
1637 int r;
1638 m_obj_params_t *p = opt->priv;
1639 const m_struct_t *desc;
1641 // We need the object desc
1642 if (!p)
1643 return M_OPT_INVALID;
1645 desc = p->desc;
1646 r = get_obj_params(name, bstr(desc->name), param, desc, p->separator,
1647 dst ? &opts : NULL);
1648 if (r < 0)
1649 return r;
1650 if (!dst)
1651 return 1;
1652 if (!opts) // no arguments given
1653 return 1;
1655 for (r = 0; opts[r]; r += 2)
1656 m_struct_set(desc, dst, opts[r], bstr(opts[r + 1]));
1658 return 1;
1662 const m_option_type_t m_option_type_obj_params = {
1663 "Object params",
1667 parse_obj_params,
1668 NULL,
1669 NULL,
1670 NULL,
1671 NULL,
1672 NULL
1675 /// Some predefined types as a definition would be quite lengthy
1677 /// Span arguments
1678 static const m_span_t m_span_params_dflts = {
1679 -1, -1
1681 static const m_option_t m_span_params_fields[] = {
1682 {"start", M_ST_OFF(m_span_t, start), CONF_TYPE_INT, M_OPT_MIN, 1, 0, NULL},
1683 {"end", M_ST_OFF(m_span_t, end), CONF_TYPE_INT, M_OPT_MIN, 1, 0, NULL},
1684 { NULL, NULL, 0, 0, 0, 0, NULL }
1686 static const struct m_struct_st m_span_opts = {
1687 "m_span",
1688 sizeof(m_span_t),
1689 &m_span_params_dflts,
1690 m_span_params_fields
1692 const m_obj_params_t m_span_params_def = {
1693 &m_span_opts,
1697 static int parse_obj_settings(struct bstr opt, struct bstr str,
1698 const m_obj_list_t *list,
1699 m_obj_settings_t **_ret, int ret_n)
1701 int r;
1702 char **plist = NULL;
1703 const m_struct_t *desc;
1704 m_obj_settings_t *ret = _ret ? *_ret : NULL;
1706 struct bstr param = bstr(NULL);
1707 int idx = bstrchr(str, '=');
1708 if (idx >= 0) {
1709 param = bstr_cut(str, idx + 1);
1710 str = bstr_splice(str, 0, idx);
1713 if (!find_obj_desc(str, list, &desc)) {
1714 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: %.*s doesn't exist.\n",
1715 BSTR_P(opt), BSTR_P(str));
1716 return M_OPT_INVALID;
1719 if (param.start) {
1720 if (!desc && _ret) {
1721 if (!bstrcmp0(param, "help")) {
1722 mp_msg(MSGT_CFGPARSER, MSGL_INFO,
1723 "Option %.*s: %.*s have no option description.\n",
1724 BSTR_P(opt), BSTR_P(str));
1725 return M_OPT_EXIT - 1;
1727 plist = talloc_zero_array(NULL, char *, 4);
1728 plist[0] = talloc_strdup(NULL, "_oldargs_");
1729 plist[1] = bstrdup0(NULL, param);
1730 } else if (desc) {
1731 r = get_obj_params(opt, str, param, desc, ':',
1732 _ret ? &plist : NULL);
1733 if (r < 0)
1734 return r;
1737 if (!_ret)
1738 return 1;
1740 ret = talloc_realloc(NULL, ret, struct m_obj_settings, ret_n + 2);
1741 memset(&ret[ret_n], 0, 2 * sizeof(m_obj_settings_t));
1742 ret[ret_n].name = bstrdup0(NULL, str);
1743 ret[ret_n].attribs = plist;
1745 *_ret = ret;
1746 return 1;
1749 static int obj_settings_list_del(struct bstr opt_name, struct bstr param,
1750 bool ambiguous_param, void *dst)
1752 char **str_list = NULL;
1753 int r, i, idx_max = 0;
1754 char *rem_id = "_removed_marker_";
1755 char name[100];
1756 assert(opt_name.len < 100);
1757 memcpy(name, opt_name.start, opt_name.len);
1758 name[opt_name.len] = 0;
1759 const m_option_t list_opt = {
1760 name, NULL, CONF_TYPE_STRING_LIST,
1761 0, 0, 0, NULL
1763 m_obj_settings_t *obj_list = dst ? VAL(dst) : NULL;
1765 if (dst && !obj_list) {
1766 mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %.*s: the list is empty.\n",
1767 BSTR_P(opt_name));
1768 return 1;
1769 } else if (obj_list) {
1770 for (idx_max = 0; obj_list[idx_max].name != NULL; idx_max++)
1771 /* NOP */;
1774 r = m_option_parse(&list_opt, opt_name, param, false, &str_list);
1775 if (r < 0 || !str_list)
1776 return r;
1778 for (r = 0; str_list[r]; r++) {
1779 int id;
1780 char *endptr;
1781 id = strtol(str_list[r], &endptr, 0);
1782 if (endptr == str_list[r]) {
1783 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));
1784 m_option_free(&list_opt, &str_list);
1785 return M_OPT_INVALID;
1787 if (!obj_list)
1788 continue;
1789 if (id >= idx_max || id < -idx_max) {
1790 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
1791 "Option %.*s: Index %d is out of range.\n",
1792 BSTR_P(opt_name), id);
1793 continue;
1795 if (id < 0)
1796 id = idx_max + id;
1797 talloc_free(obj_list[id].name);
1798 free_str_list(&(obj_list[id].attribs));
1799 obj_list[id].name = rem_id;
1802 if (!dst) {
1803 m_option_free(&list_opt, &str_list);
1804 return 1;
1807 for (i = 0; obj_list[i].name; i++) {
1808 while (obj_list[i].name == rem_id) {
1809 memmove(&obj_list[i], &obj_list[i + 1],
1810 sizeof(m_obj_settings_t) * (idx_max - i));
1811 idx_max--;
1814 obj_list = talloc_realloc(NULL, obj_list, struct m_obj_settings,
1815 idx_max + 1);
1816 VAL(dst) = obj_list;
1818 return 1;
1821 static void free_obj_settings_list(void *dst)
1823 int n;
1824 m_obj_settings_t *d;
1826 if (!dst || !VAL(dst))
1827 return;
1829 d = VAL(dst);
1830 for (n = 0; d[n].name; n++) {
1831 talloc_free(d[n].name);
1832 free_str_list(&(d[n].attribs));
1834 talloc_free(d);
1835 VAL(dst) = NULL;
1838 static int parse_obj_settings_list(const m_option_t *opt, struct bstr name,
1839 struct bstr param, bool ambiguous_param,
1840 void *dst)
1842 int len = strlen(opt->name);
1843 m_obj_settings_t *res = NULL, *queue = NULL, *head = NULL;
1844 int op = OP_NONE;
1846 // We need the objects list
1847 if (!opt->priv)
1848 return M_OPT_INVALID;
1850 if (opt->name[len - 1] == '*' && (name.len > len - 1)) {
1851 struct bstr suffix = bstr_cut(name, len - 1);
1852 if (bstrcasecmp0(suffix, "-add") == 0)
1853 op = OP_ADD;
1854 else if (bstrcasecmp0(suffix, "-pre") == 0)
1855 op = OP_PRE;
1856 else if (bstrcasecmp0(suffix, "-del") == 0)
1857 op = OP_DEL;
1858 else if (bstrcasecmp0(suffix, "-clr") == 0)
1859 op = OP_CLR;
1860 else {
1861 char prefix[len];
1862 strncpy(prefix, opt->name, len - 1);
1863 prefix[len - 1] = '\0';
1864 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
1865 "Option %.*s: unknown postfix %.*s\n"
1866 "Supported postfixes are:\n"
1867 " %s-add\n"
1868 " Append the given list to the current list\n\n"
1869 " %s-pre\n"
1870 " Prepend the given list to the current list\n\n"
1871 " %s-del x,y,...\n"
1872 " Remove the given elements. Take the list element index (starting from 0).\n"
1873 " Negative index can be used (i.e. -1 is the last element)\n\n"
1874 " %s-clr\n"
1875 " Clear the current list.\n",
1876 BSTR_P(name), BSTR_P(suffix), prefix, prefix, prefix, prefix);
1878 return M_OPT_UNKNOWN;
1882 // Clear the list ??
1883 if (op == OP_CLR) {
1884 if (dst)
1885 free_obj_settings_list(dst);
1886 return 0;
1889 if (param.len == 0)
1890 return M_OPT_MISSING_PARAM;
1892 switch (op) {
1893 case OP_ADD:
1894 if (dst)
1895 head = VAL(dst);
1896 break;
1897 case OP_PRE:
1898 if (dst)
1899 queue = VAL(dst);
1900 break;
1901 case OP_DEL:
1902 return obj_settings_list_del(name, param, false, dst);
1903 case OP_NONE:
1904 if (dst && VAL(dst))
1905 free_obj_settings_list(dst);
1906 break;
1907 default:
1908 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: FIXME\n", BSTR_P(name));
1909 return M_OPT_UNKNOWN;
1912 if (!bstrcmp0(param, "help")) {
1913 m_obj_list_t *ol = opt->priv;
1914 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available video filters:\n");
1915 for (int n = 0; ol->list[n]; n++)
1916 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-15s: %s\n",
1917 M_ST_MB(char *, ol->list[n], ol->name_off),
1918 M_ST_MB(char *, ol->list[n], ol->info_off));
1919 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1920 return M_OPT_EXIT - 1;
1923 struct bstr s = bstrdup(NULL, param);
1924 char *allocptr = s.start;
1925 int n = 0;
1926 while (s.len > 0) {
1927 struct bstr el = get_nextsep(&s, OPTION_LIST_SEPARATOR, 1);
1928 int r = parse_obj_settings(name, el, opt->priv, dst ? &res : NULL, n);
1929 if (r < 0) {
1930 talloc_free(allocptr);
1931 return r;
1933 s = bstr_cut(s, 1);
1934 n++;
1936 talloc_free(allocptr);
1937 if (n == 0)
1938 return M_OPT_INVALID;
1940 if (((opt->flags & M_OPT_MIN) && (n < opt->min)) ||
1941 ((opt->flags & M_OPT_MAX) && (n > opt->max)))
1942 return M_OPT_OUT_OF_RANGE;
1944 if (dst) {
1945 if (queue) {
1946 int qsize;
1947 for (qsize = 0; queue[qsize].name; qsize++)
1948 /* NOP */;
1949 res = talloc_realloc(NULL, res, struct m_obj_settings,
1950 qsize + n + 1);
1951 memcpy(&res[n], queue, (qsize + 1) * sizeof(m_obj_settings_t));
1952 n += qsize;
1953 talloc_free(queue);
1955 if (head) {
1956 int hsize;
1957 for (hsize = 0; head[hsize].name; hsize++)
1958 /* NOP */;
1959 head = talloc_realloc(NULL, head, struct m_obj_settings,
1960 hsize + n + 1);
1961 memcpy(&head[hsize], res, (n + 1) * sizeof(m_obj_settings_t));
1962 talloc_free(res);
1963 res = head;
1965 VAL(dst) = res;
1967 return 1;
1970 static void copy_obj_settings_list(const m_option_t *opt, void *dst,
1971 const void *src)
1973 m_obj_settings_t *d, *s;
1974 int n;
1976 if (!(dst && src))
1977 return;
1979 s = VAL(src);
1981 if (VAL(dst))
1982 free_obj_settings_list(dst);
1983 if (!s)
1984 return;
1988 for (n = 0; s[n].name; n++)
1989 /* NOP */;
1990 d = talloc_array(NULL, struct m_obj_settings, n + 1);
1991 for (n = 0; s[n].name; n++) {
1992 d[n].name = talloc_strdup(NULL, s[n].name);
1993 d[n].attribs = NULL;
1994 copy_str_list(NULL, &(d[n].attribs), &(s[n].attribs));
1996 d[n].name = NULL;
1997 d[n].attribs = NULL;
1998 VAL(dst) = d;
2001 const m_option_type_t m_option_type_obj_settings_list = {
2002 "Object settings list",
2004 sizeof(m_obj_settings_t *),
2005 M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
2006 parse_obj_settings_list,
2007 NULL,
2008 copy_obj_settings_list,
2009 copy_obj_settings_list,
2010 copy_obj_settings_list,
2011 free_obj_settings_list,
2016 static int parse_obj_presets(const m_option_t *opt, struct bstr name,
2017 struct bstr param, bool ambiguous_param,
2018 void *dst)
2020 m_obj_presets_t *obj_p = (m_obj_presets_t *)opt->priv;
2021 const m_struct_t *in_desc;
2022 const m_struct_t *out_desc;
2023 int s, i;
2024 const unsigned char *pre;
2025 char *pre_name = NULL;
2027 if (!obj_p) {
2028 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: Presets need a "
2029 "pointer to a m_obj_presets_t in the priv field.\n",
2030 BSTR_P(name));
2031 return M_OPT_PARSER_ERR;
2034 if (param.len == 0)
2035 return M_OPT_MISSING_PARAM;
2037 pre = obj_p->presets;
2038 in_desc = obj_p->in_desc;
2039 out_desc = obj_p->out_desc ? obj_p->out_desc : obj_p->in_desc;
2040 s = in_desc->size;
2042 if (!bstrcmp0(param, "help")) {
2043 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available presets for %s->%.*s:",
2044 out_desc->name, BSTR_P(name));
2045 for (pre = obj_p->presets;
2046 (pre_name = M_ST_MB(char *, pre, obj_p->name_off)); pre += s)
2047 mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", pre_name);
2048 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
2049 return M_OPT_EXIT - 1;
2052 for (pre_name = M_ST_MB(char *, pre, obj_p->name_off); pre_name;
2053 pre += s, pre_name = M_ST_MB(char *, pre, obj_p->name_off))
2054 if (!bstrcmp0(param, pre_name))
2055 break;
2056 if (!pre_name) {
2057 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2058 "Option %.*s: There is no preset named %.*s\n"
2059 "Available presets are:", BSTR_P(name), BSTR_P(param));
2060 for (pre = obj_p->presets;
2061 (pre_name = M_ST_MB(char *, pre, obj_p->name_off)); pre += s)
2062 mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", pre_name);
2063 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
2064 return M_OPT_INVALID;
2067 if (!dst)
2068 return 1;
2070 for (i = 0; in_desc->fields[i].name; i++) {
2071 const m_option_t *out_opt = m_option_list_find(out_desc->fields,
2072 in_desc->fields[i].name);
2073 if (!out_opt) {
2074 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2075 "Option %.*s: Unable to find the target option for field %s.\n"
2076 "Please report this to the developers.\n",
2077 BSTR_P(name), in_desc->fields[i].name);
2078 return M_OPT_PARSER_ERR;
2080 m_option_copy(out_opt, M_ST_MB_P(dst, out_opt->p),
2081 M_ST_MB_P(pre, in_desc->fields[i].p));
2083 return 1;
2087 const m_option_type_t m_option_type_obj_presets = {
2088 "Object presets",
2092 parse_obj_presets,
2093 NULL,
2094 NULL,
2095 NULL,
2096 NULL,
2097 NULL
2100 static int parse_custom_url(const m_option_t *opt, struct bstr name,
2101 struct bstr url, bool ambiguous_param, void *dst)
2103 int r;
2104 m_struct_t *desc = opt->priv;
2106 if (!desc) {
2107 mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %.*s: Custom URL needs "
2108 "a pointer to a m_struct_t in the priv field.\n", BSTR_P(name));
2109 return M_OPT_PARSER_ERR;
2112 // extract the protocol
2113 int idx = bstr_find0(url, "://");
2114 if (idx < 0) {
2115 // Filename only
2116 if (m_option_list_find(desc->fields, "filename")) {
2117 m_struct_set(desc, dst, "filename", url);
2118 return 1;
2120 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2121 "Option %.*s: URL doesn't have a valid protocol!\n",
2122 BSTR_P(name));
2123 return M_OPT_INVALID;
2125 struct bstr ptr1 = bstr_cut(url, idx + 3);
2126 if (m_option_list_find(desc->fields, "string")) {
2127 if (ptr1.len > 0) {
2128 m_struct_set(desc, dst, "string", ptr1);
2129 return 1;
2132 if (dst && m_option_list_find(desc->fields, "protocol")) {
2133 r = m_struct_set(desc, dst, "protocol", bstr_splice(url, 0, idx));
2134 if (r < 0) {
2135 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2136 "Option %.*s: Error while setting protocol.\n",
2137 BSTR_P(name));
2138 return r;
2142 // check if a username:password is given
2143 idx = bstrchr(ptr1, '/');
2144 if (idx < 0)
2145 idx = ptr1.len;
2146 struct bstr hostpart = bstr_splice(ptr1, 0, idx);
2147 struct bstr path = bstr_cut(ptr1, idx);
2148 idx = bstrchr(hostpart, '@');
2149 if (idx >= 0) {
2150 // We got something, at least a username...
2151 if (!m_option_list_find(desc->fields, "username")) {
2152 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
2153 "Option %.*s: This URL doesn't have a username part.\n",
2154 BSTR_P(name));
2155 // skip
2156 } else {
2157 struct bstr userpass = bstr_splice(hostpart, 0, idx);
2158 idx = bstrchr(userpass, ':');
2159 if (idx >= 0) {
2160 // We also have a password
2161 if (!m_option_list_find(desc->fields, "password")) {
2162 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
2163 "Option %.*s: This URL doesn't have a password part.\n",
2164 BSTR_P(name));
2165 // skip
2166 } else { // Username and password
2167 if (dst) {
2168 r = m_struct_set(desc, dst, "username",
2169 bstr_splice(userpass, 0, idx));
2170 if (r < 0) {
2171 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2172 "Option %.*s: Error while setting username.\n",
2173 BSTR_P(name));
2174 return r;
2176 r = m_struct_set(desc, dst, "password",
2177 bstr_splice(userpass, idx+1,
2178 userpass.len));
2179 if (r < 0) {
2180 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2181 "Option %.*s: Error while setting password.\n",
2182 BSTR_P(name));
2183 return r;
2187 } else { // User name only
2188 r = m_struct_set(desc, dst, "username", userpass);
2189 if (r < 0) {
2190 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2191 "Option %.*s: Error while setting username.\n",
2192 BSTR_P(name));
2193 return r;
2197 hostpart = bstr_cut(hostpart, idx + 1);
2200 // Before looking for a port number check if we have an IPv6 type
2201 // numeric address.
2202 // In an IPv6 URL the numeric address should be inside square braces.
2203 int idx1 = bstrchr(hostpart, '[');
2204 int idx2 = bstrchr(hostpart, ']');
2205 struct bstr portstr = hostpart;
2206 bool v6addr = false;
2207 if (idx1 >= 0 && idx2 >= 0 && idx1 < idx2) {
2208 // we have an IPv6 numeric address
2209 portstr = bstr_cut(hostpart, idx2);
2210 hostpart = bstr_splice(hostpart, idx1 + 1, idx2);
2211 v6addr = true;
2214 idx = bstrchr(portstr, ':');
2215 if (idx >= 0) {
2216 if (!v6addr)
2217 hostpart = bstr_splice(hostpart, 0, idx);
2218 // We have an URL beginning like http://www.hostname.com:1212
2219 // Get the port number
2220 if (!m_option_list_find(desc->fields, "port")) {
2221 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
2222 "Option %.*s: This URL doesn't have a port part.\n",
2223 BSTR_P(name));
2224 // skip
2225 } else {
2226 if (dst) {
2227 int p = bstrtoll(bstr_cut(portstr, idx + 1), NULL, 0);
2228 char tmp[100];
2229 snprintf(tmp, 99, "%d", p);
2230 r = m_struct_set(desc, dst, "port", bstr(tmp));
2231 if (r < 0) {
2232 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2233 "Option %.*s: Error while setting port.\n",
2234 BSTR_P(name));
2235 return r;
2240 // Get the hostname
2241 if (hostpart.len > 0) {
2242 if (!m_option_list_find(desc->fields, "hostname")) {
2243 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
2244 "Option %.*s: This URL doesn't have a hostname part.\n",
2245 BSTR_P(name));
2246 // skip
2247 } else {
2248 r = m_struct_set(desc, dst, "hostname", hostpart);
2249 if (r < 0) {
2250 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2251 "Option %.*s: Error while setting hostname.\n",
2252 BSTR_P(name));
2253 return r;
2257 // Look if a path is given
2258 if (path.len > 1) { // not just "/"
2259 // copy the path/filename in the URL container
2260 if (!m_option_list_find(desc->fields, "filename")) {
2261 mp_msg(MSGT_CFGPARSER, MSGL_WARN,
2262 "Option %.*s: This URL doesn't have a filename part.\n",
2263 BSTR_P(name));
2264 // skip
2265 } else {
2266 if (dst) {
2267 char *fname = bstrdup0(NULL, bstr_cut(path, 1));
2268 url_unescape_string(fname, fname);
2269 r = m_struct_set(desc, dst, "filename", bstr(fname));
2270 talloc_free(fname);
2271 if (r < 0) {
2272 mp_msg(MSGT_CFGPARSER, MSGL_ERR,
2273 "Option %.*s: Error while setting filename.\n",
2274 BSTR_P(name));
2275 return r;
2280 return 1;
2283 /// TODO : Write the other needed funcs for 'normal' options
2284 const m_option_type_t m_option_type_custom_url = {
2285 "Custom URL",
2289 parse_custom_url,
2290 NULL,
2291 NULL,
2292 NULL,
2293 NULL,
2294 NULL