1 /*
2 * This file is part of mpv.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv 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 Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /// \file
19 /// \ingroup Options
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <limits.h>
26 #include <math.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <limits.h>
30 #include <inttypes.h>
31 #include <unistd.h>
32 #include <assert.h>
33
34 #include <libavutil/common.h>
35
36 #include "libmpv/client.h"
37 #include "player/client.h"
38
39 #include "mpv_talloc.h"
40 #include "common/common.h"
41 #include "common/msg.h"
42 #include "common/msg_control.h"
43 #include "misc/json.h"
44 #include "misc/node.h"
45 #include "m_option.h"
46 #include "m_config_frontend.h"
47
48 #if HAVE_DOS_PATHS
49 #define OPTION_PATH_SEPARATOR ';'
50 #else
51 #define OPTION_PATH_SEPARATOR ':'
52 #endif
53
54 const char m_option_path_separator = OPTION_PATH_SEPARATOR;
55
56 // For integer types: since min/max are floats and may not be able to represent
57 // the real min/max, and since opt.min/.max may use +/-INFINITY, some care has
58 // to be taken. (Also tricky rounding.)
59 #define OPT_INT_MIN(opt, T, Tm) ((opt)->min < (opt)->max \
60 ? ((opt)->min <= (double)(Tm) ? (Tm) : (T)((opt)->min)) : (Tm))
61 #define OPT_INT_MAX(opt, T, Tm) ((opt)->min < (opt)->max \
62 ? ((opt)->max >= (double)(Tm) ? (Tm) : (T)((opt)->max)) : (Tm))
63
m_option_parse(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)64 int m_option_parse(struct mp_log *log, const m_option_t *opt,
65 struct bstr name, struct bstr param, void *dst)
66 {
67 int r = M_OPT_INVALID;
68 if (bstr_equals0(param, "help") && opt->help) {
69 r = opt->help(log, opt, name);
70 if (r < 0)
71 return r;
72 }
73
74 r = opt->type->parse(log, opt, name, param, dst);
75 if (r < 0)
76 return r;
77
78 if (opt->validate) {
79 r = opt->validate(log, opt, name, dst);
80 if (r < 0) {
81 if (opt->type->free)
82 opt->type->free(dst);
83 return r;
84 }
85 }
86 return 1;
87 }
88
m_option_strerror(int code)89 char *m_option_strerror(int code)
90 {
91 switch (code) {
92 case M_OPT_UNKNOWN:
93 return "option not found";
94 case M_OPT_MISSING_PARAM:
95 return "option requires parameter";
96 case M_OPT_INVALID:
97 return "option parameter could not be parsed";
98 case M_OPT_OUT_OF_RANGE:
99 return "parameter is outside values allowed for option";
100 case M_OPT_DISALLOW_PARAM:
101 return "option doesn't take a parameter";
102 default:
103 return "parser error";
104 }
105 }
106
m_option_required_params(const m_option_t * opt)107 int m_option_required_params(const m_option_t *opt)
108 {
109 if (opt->type->flags & M_OPT_TYPE_OPTIONAL_PARAM)
110 return 0;
111 if (opt->flags & M_OPT_OPTIONAL_PARAM)
112 return 0;
113 if (opt->type == &m_option_type_choice) {
114 struct m_opt_choice_alternatives *alt;
115 for (alt = opt->priv; alt->name; alt++) {
116 if (strcmp(alt->name, "yes") == 0)
117 return 0;
118 }
119 }
120 return 1;
121 }
122
m_option_set_node_or_string(struct mp_log * log,const m_option_t * opt,const char * name,void * dst,struct mpv_node * src)123 int m_option_set_node_or_string(struct mp_log *log, const m_option_t *opt,
124 const char *name, void *dst, struct mpv_node *src)
125 {
126 if (src->format == MPV_FORMAT_STRING) {
127 // The af and vf option unfortunately require this, because the
128 // option name includes the "action".
129 bstr optname = bstr0(name), a, b;
130 if (bstr_split_tok(optname, "/", &a, &b))
131 optname = b;
132 return m_option_parse(log, opt, optname, bstr0(src->u.string), dst);
133 } else {
134 return m_option_set_node(opt, dst, src);
135 }
136 }
137
138 // Default function that just does a memcpy
139
copy_opt(const m_option_t * opt,void * dst,const void * src)140 static void copy_opt(const m_option_t *opt, void *dst, const void *src)
141 {
142 if (dst && src)
143 memcpy(dst, src, opt->type->size);
144 }
145
146 // Bool
147
148 #define VAL(x) (*(bool *)(x))
149
parse_bool(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)150 static int parse_bool(struct mp_log *log, const m_option_t *opt,
151 struct bstr name, struct bstr param, void *dst)
152 {
153 if (bstr_equals0(param, "yes") || !param.len) {
154 if (dst)
155 VAL(dst) = 1;
156 return 1;
157 }
158 if (bstr_equals0(param, "no")) {
159 if (dst)
160 VAL(dst) = 0;
161 return 1;
162 }
163 bool is_help = bstr_equals0(param, "help");
164 if (is_help) {
165 mp_info(log, "Valid values for %.*s flag are:\n", BSTR_P(name));
166 } else {
167 mp_fatal(log, "Invalid parameter for %.*s flag: %.*s\n",
168 BSTR_P(name), BSTR_P(param));
169 mp_info(log, "Valid values are:\n");
170 }
171 mp_info(log, " yes\n");
172 mp_info(log, " no\n");
173 mp_info(log, " (passing nothing)\n");
174 return is_help ? M_OPT_EXIT : M_OPT_INVALID;
175 }
176
print_bool(const m_option_t * opt,const void * val)177 static char *print_bool(const m_option_t *opt, const void *val)
178 {
179 return talloc_strdup(NULL, VAL(val) ? "yes" : "no");
180 }
181
add_bool(const m_option_t * opt,void * val,double add,bool wrap)182 static void add_bool(const m_option_t *opt, void *val, double add, bool wrap)
183 {
184 if (fabs(add) < 0.5)
185 return;
186 bool state = !!VAL(val);
187 state = wrap ? !state : add > 0;
188 VAL(val) = state ? 1 : 0;
189 }
190
bool_set(const m_option_t * opt,void * dst,struct mpv_node * src)191 static int bool_set(const m_option_t *opt, void *dst, struct mpv_node *src)
192 {
193 if (src->format != MPV_FORMAT_FLAG)
194 return M_OPT_UNKNOWN;
195 VAL(dst) = !!src->u.flag;
196 return 1;
197 }
198
bool_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)199 static int bool_get(const m_option_t *opt, void *ta_parent,
200 struct mpv_node *dst, void *src)
201 {
202 dst->format = MPV_FORMAT_FLAG;
203 dst->u.flag = !!VAL(src);
204 return 1;
205 }
206
bool_equal(const m_option_t * opt,void * a,void * b)207 static bool bool_equal(const m_option_t *opt, void *a, void *b)
208 {
209 return VAL(a) == VAL(b);
210 }
211
212 const m_option_type_t m_option_type_bool = {
213 .name = "Flag", // same as m_option_type_flag; transparent to user
214 .size = sizeof(bool),
215 .flags = M_OPT_TYPE_OPTIONAL_PARAM | M_OPT_TYPE_CHOICE,
216 .parse = parse_bool,
217 .print = print_bool,
218 .copy = copy_opt,
219 .add = add_bool,
220 .set = bool_set,
221 .get = bool_get,
222 .equal = bool_equal,
223 };
224
225 #undef VAL
226
227 // Flag
228
229 #define VAL(x) (*(int *)(x))
230
parse_flag(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)231 static int parse_flag(struct mp_log *log, const m_option_t *opt,
232 struct bstr name, struct bstr param, void *dst)
233 {
234 bool bdst = false;
235 int r = parse_bool(log, opt, name, param, &bdst);
236 if (dst)
237 VAL(dst) = bdst;
238 return r;
239 }
240
print_flag(const m_option_t * opt,const void * val)241 static char *print_flag(const m_option_t *opt, const void *val)
242 {
243 return print_bool(opt, &(bool){VAL(val)});
244 }
245
add_flag(const m_option_t * opt,void * val,double add,bool wrap)246 static void add_flag(const m_option_t *opt, void *val, double add, bool wrap)
247 {
248 bool bval = VAL(val);
249 add_bool(opt, &bval, add, wrap);
250 VAL(val) = bval;
251 }
252
flag_set(const m_option_t * opt,void * dst,struct mpv_node * src)253 static int flag_set(const m_option_t *opt, void *dst, struct mpv_node *src)
254 {
255 bool bdst = false;
256 int r = bool_set(opt, &bdst, src);
257 if (r >= 0)
258 VAL(dst) = bdst;
259 return r;
260 }
261
flag_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)262 static int flag_get(const m_option_t *opt, void *ta_parent,
263 struct mpv_node *dst, void *src)
264 {
265 return bool_get(opt, ta_parent, dst, &(bool){VAL(src)});
266 }
267
flag_equal(const m_option_t * opt,void * a,void * b)268 static bool flag_equal(const m_option_t *opt, void *a, void *b)
269 {
270 return VAL(a) == VAL(b);
271 }
272
273 const m_option_type_t m_option_type_flag = {
274 // need yes or no in config files
275 .name = "Flag",
276 .size = sizeof(int),
277 .flags = M_OPT_TYPE_OPTIONAL_PARAM | M_OPT_TYPE_CHOICE,
278 .parse = parse_flag,
279 .print = print_flag,
280 .copy = copy_opt,
281 .add = add_flag,
282 .set = flag_set,
283 .get = flag_get,
284 .equal = flag_equal,
285 };
286
287 // Integer
288
289 #undef VAL
290
clamp_longlong(const m_option_t * opt,long long i_min,long long i_max,void * val)291 static int clamp_longlong(const m_option_t *opt, long long i_min, long long i_max,
292 void *val)
293 {
294 long long v = *(long long *)val;
295 int r = 0;
296 long long min = OPT_INT_MIN(opt, long long, i_min);
297 long long max = OPT_INT_MAX(opt, long long, i_max);
298 if (v > max) {
299 v = max;
300 r = M_OPT_OUT_OF_RANGE;
301 }
302 if (v < min) {
303 v = min;
304 r = M_OPT_OUT_OF_RANGE;
305 }
306 *(long long *)val = v;
307 return r;
308 }
309
parse_longlong(struct mp_log * log,const m_option_t * opt,long long i_min,long long i_max,struct bstr name,struct bstr param,void * dst)310 static int parse_longlong(struct mp_log *log, const m_option_t *opt,
311 long long i_min, long long i_max,
312 struct bstr name, struct bstr param, void *dst)
313 {
314 if (param.len == 0)
315 return M_OPT_MISSING_PARAM;
316
317 struct bstr rest;
318 long long tmp_int = bstrtoll(param, &rest, 10);
319 if (rest.len)
320 tmp_int = bstrtoll(param, &rest, 0);
321 if (rest.len) {
322 mp_err(log, "The %.*s option must be an integer: %.*s\n",
323 BSTR_P(name), BSTR_P(param));
324 return M_OPT_INVALID;
325 }
326
327 long long min = OPT_INT_MIN(opt, long long, i_min);
328 if (tmp_int < min) {
329 mp_err(log, "The %.*s option must be >= %lld: %.*s\n",
330 BSTR_P(name), min, BSTR_P(param));
331 return M_OPT_OUT_OF_RANGE;
332 }
333
334 long long max = OPT_INT_MAX(opt, long long, i_max);
335 if (tmp_int > max) {
336 mp_err(log, "The %.*s option must be <= %lld: %.*s\n",
337 BSTR_P(name), max, BSTR_P(param));
338 return M_OPT_OUT_OF_RANGE;
339 }
340
341 if (dst)
342 *(long long *)dst = tmp_int;
343
344 return 1;
345 }
346
clamp_int64(const m_option_t * opt,void * val)347 static int clamp_int64(const m_option_t *opt, void *val)
348 {
349 long long tmp = *(int64_t *)val;
350 int r = clamp_longlong(opt, INT64_MIN, INT64_MAX, &tmp);
351 *(int64_t *)val = tmp;
352 return r;
353 }
354
parse_int(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)355 static int parse_int(struct mp_log *log, const m_option_t *opt,
356 struct bstr name, struct bstr param, void *dst)
357 {
358 long long tmp;
359 int r = parse_longlong(log, opt, INT_MIN, INT_MAX, name, param, &tmp);
360 if (r >= 0 && dst)
361 *(int *)dst = tmp;
362 return r;
363 }
364
parse_int64(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)365 static int parse_int64(struct mp_log *log, const m_option_t *opt,
366 struct bstr name, struct bstr param, void *dst)
367 {
368 long long tmp;
369 int r = parse_longlong(log, opt, INT64_MIN, INT64_MAX, name, param, &tmp);
370 if (r >= 0 && dst)
371 *(int64_t *)dst = tmp;
372 return r;
373 }
374
print_int(const m_option_t * opt,const void * val)375 static char *print_int(const m_option_t *opt, const void *val)
376 {
377 if (opt->type->size == sizeof(int64_t))
378 return talloc_asprintf(NULL, "%"PRId64, *(const int64_t *)val);
379 return talloc_asprintf(NULL, "%d", *(const int *)val);
380 }
381
add_int64(const m_option_t * opt,void * val,double add,bool wrap)382 static void add_int64(const m_option_t *opt, void *val, double add, bool wrap)
383 {
384 int64_t v = *(int64_t *)val;
385
386 clamp_int64(opt, &v);
387
388 v = v + add;
389
390 bool is64 = opt->type->size == sizeof(int64_t);
391 int64_t nmin = is64 ? INT64_MIN : INT_MIN;
392 int64_t nmax = is64 ? INT64_MAX : INT_MAX;
393
394 int64_t min = OPT_INT_MIN(opt, int64_t, nmin);
395 int64_t max = OPT_INT_MAX(opt, int64_t, nmax);
396
397 if (v < min)
398 v = wrap ? max : min;
399 if (v > max)
400 v = wrap ? min : max;
401
402 *(int64_t *)val = v;
403 }
404
add_int(const m_option_t * opt,void * val,double add,bool wrap)405 static void add_int(const m_option_t *opt, void *val, double add, bool wrap)
406 {
407 int64_t tmp = *(int *)val;
408 add_int64(opt, &tmp, add, wrap);
409 *(int *)val = tmp;
410 }
411
multiply_int64(const m_option_t * opt,void * val,double f)412 static void multiply_int64(const m_option_t *opt, void *val, double f)
413 {
414 double v = *(int64_t *)val * f;
415 int64_t iv = v;
416 if (v < INT64_MIN)
417 iv = INT64_MIN;
418 if (v >= (double)INT64_MAX)
419 iv = INT64_MAX;
420 *(int64_t *)val = iv;
421 clamp_int64(opt, val);
422 }
423
multiply_int(const m_option_t * opt,void * val,double f)424 static void multiply_int(const m_option_t *opt, void *val, double f)
425 {
426 int64_t tmp = *(int *)val;
427 multiply_int64(opt, &tmp, f);
428 *(int *)val = MPCLAMP(tmp, INT_MIN, INT_MAX);
429 }
430
int64_set(const m_option_t * opt,void * dst,struct mpv_node * src)431 static int int64_set(const m_option_t *opt, void *dst, struct mpv_node *src)
432 {
433 if (src->format != MPV_FORMAT_INT64)
434 return M_OPT_UNKNOWN;
435 int64_t val = src->u.int64;
436 if (val < OPT_INT_MIN(opt, int64_t, INT64_MIN))
437 return M_OPT_OUT_OF_RANGE;
438 if (val > OPT_INT_MAX(opt, int64_t, INT64_MAX))
439 return M_OPT_OUT_OF_RANGE;
440 *(int64_t *)dst = val;
441 return 1;
442 }
443
int_set(const m_option_t * opt,void * dst,struct mpv_node * src)444 static int int_set(const m_option_t *opt, void *dst, struct mpv_node *src)
445 {
446 int64_t val;
447 int r = int64_set(opt, &val, src);
448 if (r >= 0) {
449 if (val < INT_MIN || val > INT_MAX)
450 return M_OPT_OUT_OF_RANGE;
451 *(int *)dst = val;
452 }
453 return r;
454 }
455
int64_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)456 static int int64_get(const m_option_t *opt, void *ta_parent,
457 struct mpv_node *dst, void *src)
458 {
459 dst->format = MPV_FORMAT_INT64;
460 dst->u.int64 = *(int64_t *)src;
461 return 1;
462 }
463
int_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)464 static int int_get(const m_option_t *opt, void *ta_parent,
465 struct mpv_node *dst, void *src)
466 {
467 dst->format = MPV_FORMAT_INT64;
468 dst->u.int64 = *(int *)src;
469 return 1;
470 }
471
int_equal(const m_option_t * opt,void * a,void * b)472 static bool int_equal(const m_option_t *opt, void *a, void *b)
473 {
474 return *(int *)a == *(int *)b;
475 }
476
int64_equal(const m_option_t * opt,void * a,void * b)477 static bool int64_equal(const m_option_t *opt, void *a, void *b)
478 {
479 return *(int64_t *)a == *(int64_t *)b;
480 }
481
482 const m_option_type_t m_option_type_int = {
483 .name = "Integer",
484 .flags = M_OPT_TYPE_USES_RANGE,
485 .size = sizeof(int),
486 .parse = parse_int,
487 .print = print_int,
488 .copy = copy_opt,
489 .add = add_int,
490 .multiply = multiply_int,
491 .set = int_set,
492 .get = int_get,
493 .equal = int_equal,
494 };
495
496 const m_option_type_t m_option_type_int64 = {
497 .name = "Integer64",
498 .flags = M_OPT_TYPE_USES_RANGE,
499 .size = sizeof(int64_t),
500 .parse = parse_int64,
501 .print = print_int,
502 .copy = copy_opt,
503 .add = add_int64,
504 .multiply = multiply_int64,
505 .set = int64_set,
506 .get = int64_get,
507 .equal = int64_equal,
508 };
509
parse_byte_size(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)510 static int parse_byte_size(struct mp_log *log, const m_option_t *opt,
511 struct bstr name, struct bstr param, void *dst)
512 {
513 if (param.len == 0)
514 return M_OPT_MISSING_PARAM;
515
516 struct bstr r;
517 long long tmp_int = bstrtoll(param, &r, 0);
518 int64_t unit = 1;
519 if (r.len) {
520 if (bstrcasecmp0(r, "b") == 0) {
521 unit = 1;
522 } else if (bstrcasecmp0(r, "kib") == 0 || bstrcasecmp0(r, "k") == 0) {
523 unit = 1024;
524 } else if (bstrcasecmp0(r, "mib") == 0 || bstrcasecmp0(r, "m") == 0) {
525 unit = 1024 * 1024;
526 } else if (bstrcasecmp0(r, "gib") == 0 || bstrcasecmp0(r, "g") == 0) {
527 unit = 1024 * 1024 * 1024;
528 } else if (bstrcasecmp0(r, "tib") == 0 || bstrcasecmp0(r, "t") == 0) {
529 unit = 1024 * 1024 * 1024 * 1024LL;
530 } else {
531 mp_err(log, "The %.*s option must be an integer: %.*s\n",
532 BSTR_P(name), BSTR_P(param));
533 mp_err(log, "The following suffixes are also allowed: "
534 "KiB, MiB, GiB, TiB, B, K, M, G, T.\n");
535 return M_OPT_INVALID;
536 }
537 }
538
539 if (tmp_int < 0) {
540 mp_err(log, "The %.*s option does not support negative numbers: %.*s\n",
541 BSTR_P(name), BSTR_P(param));
542 return M_OPT_OUT_OF_RANGE;
543 }
544
545 if (INT64_MAX / unit < tmp_int) {
546 mp_err(log, "The %.*s option overflows: %.*s\n",
547 BSTR_P(name), BSTR_P(param));
548 return M_OPT_OUT_OF_RANGE;
549 }
550
551 tmp_int *= unit;
552
553 int64_t min = OPT_INT_MIN(opt, int64_t, INT64_MIN);
554 if (tmp_int < min) {
555 mp_err(log, "The %.*s option must be >= %"PRId64": %.*s\n",
556 BSTR_P(name), min, BSTR_P(param));
557 return M_OPT_OUT_OF_RANGE;
558 }
559
560 int64_t max = OPT_INT_MAX(opt, int64_t, INT64_MAX);
561 if (tmp_int > max) {
562 mp_err(log, "The %.*s option must be <= %"PRId64": %.*s\n",
563 BSTR_P(name), max, BSTR_P(param));
564 return M_OPT_OUT_OF_RANGE;
565 }
566
567 if (dst)
568 *(int64_t *)dst = tmp_int;
569
570 return 1;
571 }
572
format_file_size(int64_t size)573 char *format_file_size(int64_t size)
574 {
575 double s = size;
576 if (size < 1024)
577 return talloc_asprintf(NULL, "%.0f B", s);
578
579 if (size < (1024 * 1024))
580 return talloc_asprintf(NULL, "%.3f KiB", s / (1024.0));
581
582 if (size < (1024 * 1024 * 1024))
583 return talloc_asprintf(NULL, "%.3f MiB", s / (1024.0 * 1024.0));
584
585 if (size < (1024LL * 1024LL * 1024LL * 1024LL))
586 return talloc_asprintf(NULL, "%.3f GiB", s / (1024.0 * 1024.0 * 1024.0));
587
588 return talloc_asprintf(NULL, "%.3f TiB", s / (1024.0 * 1024.0 * 1024.0 * 1024.0));
589 }
590
pretty_print_byte_size(const m_option_t * opt,const void * val)591 static char *pretty_print_byte_size(const m_option_t *opt, const void *val)
592 {
593 return format_file_size(*(int64_t *)val);
594 }
595
596 const m_option_type_t m_option_type_byte_size = {
597 .name = "ByteSize",
598 .flags = M_OPT_TYPE_USES_RANGE,
599 .size = sizeof(int64_t),
600 .parse = parse_byte_size,
601 .print = print_int,
602 .pretty_print = pretty_print_byte_size,
603 .copy = copy_opt,
604 .add = add_int64,
605 .multiply = multiply_int64,
606 .set = int64_set,
607 .get = int64_get,
608 .equal = int64_equal,
609 };
610
m_opt_choice_str(const struct m_opt_choice_alternatives * choices,int value)611 const char *m_opt_choice_str(const struct m_opt_choice_alternatives *choices,
612 int value)
613 {
614 for (const struct m_opt_choice_alternatives *c = choices; c->name; c++) {
615 if (c->value == value)
616 return c->name;
617 }
618 return NULL;
619 }
620
print_choice_values(struct mp_log * log,const struct m_option * opt)621 static void print_choice_values(struct mp_log *log, const struct m_option *opt)
622 {
623 struct m_opt_choice_alternatives *alt = opt->priv;
624 for ( ; alt->name; alt++)
625 mp_info(log, " %s\n", alt->name[0] ? alt->name : "(passing nothing)");
626 if (opt->min < opt->max)
627 mp_info(log, " %g-%g (integer range)\n", opt->min, opt->max);
628 }
629
parse_choice(struct mp_log * log,const struct m_option * opt,struct bstr name,struct bstr param,void * dst)630 static int parse_choice(struct mp_log *log, const struct m_option *opt,
631 struct bstr name, struct bstr param, void *dst)
632 {
633 struct m_opt_choice_alternatives *alt = opt->priv;
634 for ( ; alt->name; alt++) {
635 if (!bstrcmp0(param, alt->name))
636 break;
637 }
638 if (!alt->name && param.len == 0) {
639 // allow flag-style options, e.g. "--mute" implies "--mute=yes"
640 for (alt = opt->priv; alt->name; alt++) {
641 if (!strcmp("yes", alt->name))
642 break;
643 }
644 }
645 if (!alt->name) {
646 if (!bstrcmp0(param, "help")) {
647 mp_info(log, "Valid values for option %.*s are:\n", BSTR_P(name));
648 print_choice_values(log, opt);
649 return M_OPT_EXIT;
650 }
651 if (param.len == 0)
652 return M_OPT_MISSING_PARAM;
653 if (opt->min < opt->max) {
654 long long val;
655 if (parse_longlong(mp_null_log, opt, INT_MIN, INT_MAX, name, param,
656 &val) == 1)
657 {
658 if (dst)
659 *(int *)dst = val;
660 return 1;
661 }
662 }
663 mp_fatal(log, "Invalid value for option %.*s: %.*s\n",
664 BSTR_P(name), BSTR_P(param));
665 mp_info(log, "Valid values are:\n");
666 print_choice_values(log, opt);
667 return M_OPT_INVALID;
668 }
669 if (dst)
670 *(int *)dst = alt->value;
671
672 return 1;
673 }
674
choice_get_min_max(const struct m_option * opt,int * min,int * max)675 static void choice_get_min_max(const struct m_option *opt, int *min, int *max)
676 {
677 assert(opt->type == &m_option_type_choice);
678 *min = INT_MAX;
679 *max = INT_MIN;
680 for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++) {
681 *min = MPMIN(*min, alt->value);
682 *max = MPMAX(*max, alt->value);
683 }
684 if (opt->min < opt->max) {
685 *min = MPMIN(*min, opt->min);
686 *max = MPMAX(*max, opt->max);
687 }
688 }
689
check_choice(int dir,int val,bool * found,int * best,int choice)690 static void check_choice(int dir, int val, bool *found, int *best, int choice)
691 {
692 if ((dir == -1 && (!(*found) || choice > (*best)) && choice < val) ||
693 (dir == +1 && (!(*found) || choice < (*best)) && choice > val))
694 {
695 *found = true;
696 *best = choice;
697 }
698 }
699
add_choice(const m_option_t * opt,void * val,double add,bool wrap)700 static void add_choice(const m_option_t *opt, void *val, double add, bool wrap)
701 {
702 assert(opt->type == &m_option_type_choice);
703 int dir = add > 0 ? +1 : -1;
704 bool found = false;
705 int ival = *(int *)val;
706 int best = 0; // init. value unused
707
708 if (fabs(add) < 0.5)
709 return;
710
711 if (opt->min < opt->max) {
712 int newval = ival + add;
713 if (ival >= opt->min && ival <= opt->max &&
714 newval >= opt->min && newval <= opt->max)
715 {
716 found = true;
717 best = newval;
718 } else {
719 check_choice(dir, ival, &found, &best, opt->min);
720 check_choice(dir, ival, &found, &best, opt->max);
721 }
722 }
723
724 for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++)
725 check_choice(dir, ival, &found, &best, alt->value);
726
727 if (!found) {
728 int min, max;
729 choice_get_min_max(opt, &min, &max);
730 best = (dir == -1) ^ wrap ? min : max;
731 }
732
733 *(int *)val = best;
734 }
735
choice_set(const m_option_t * opt,void * dst,struct mpv_node * src)736 static int choice_set(const m_option_t *opt, void *dst, struct mpv_node *src)
737 {
738 char buf[80];
739 char *src_str = NULL;
740 if (src->format == MPV_FORMAT_INT64) {
741 snprintf(buf, sizeof(buf), "%" PRId64, src->u.int64);
742 src_str = buf;
743 } else if (src->format == MPV_FORMAT_STRING) {
744 src_str = src->u.string;
745 } else if (src->format == MPV_FORMAT_FLAG) {
746 src_str = src->u.flag ? "yes" : "no";
747 }
748 if (!src_str)
749 return M_OPT_UNKNOWN;
750 int val = 0;
751 int r = parse_choice(mp_null_log, opt, (bstr){0}, bstr0(src_str), &val);
752 if (r >= 0)
753 *(int *)dst = val;
754 return r;
755 }
756
get_choice(const m_option_t * opt,const void * val,int * out_val)757 static struct m_opt_choice_alternatives *get_choice(const m_option_t *opt,
758 const void *val, int *out_val)
759 {
760 int v = *(int *)val;
761 struct m_opt_choice_alternatives *alt;
762 for (alt = opt->priv; alt->name; alt++) {
763 if (alt->value == v)
764 return alt;
765 }
766 if (opt->min < opt->max) {
767 if (v >= opt->min && v <= opt->max) {
768 *out_val = v;
769 return NULL;
770 }
771 }
772 abort();
773 }
774
choice_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)775 static int choice_get(const m_option_t *opt, void *ta_parent,
776 struct mpv_node *dst, void *src)
777 {
778 int ival = 0;
779 struct m_opt_choice_alternatives *alt = get_choice(opt, src, &ival);
780 // If a choice string looks like a number, return it as number
781 if (alt) {
782 char *end = NULL;
783 ival = strtol(alt->name, &end, 10);
784 if (end && !end[0])
785 alt = NULL;
786 }
787 if (alt) {
788 int b = -1;
789 if (strcmp(alt->name, "yes") == 0) {
790 b = 1;
791 } else if (strcmp(alt->name, "no") == 0) {
792 b = 0;
793 }
794 if (b >= 0) {
795 dst->format = MPV_FORMAT_FLAG;
796 dst->u.flag = b;
797 } else {
798 dst->format = MPV_FORMAT_STRING;
799 dst->u.string = talloc_strdup(ta_parent, alt->name);
800 }
801 } else {
802 dst->format = MPV_FORMAT_INT64;
803 dst->u.int64 = ival;
804 }
805 return 1;
806 }
807
print_choice(const m_option_t * opt,const void * val)808 static char *print_choice(const m_option_t *opt, const void *val)
809 {
810 int ival = 0;
811 struct m_opt_choice_alternatives *alt = get_choice(opt, val, &ival);
812 return alt ? talloc_strdup(NULL, alt->name)
813 : talloc_asprintf(NULL, "%d", ival);
814 }
815
816 const struct m_option_type m_option_type_choice = {
817 .name = "Choice",
818 .size = sizeof(int),
819 .flags = M_OPT_TYPE_CHOICE | M_OPT_TYPE_USES_RANGE,
820 .parse = parse_choice,
821 .print = print_choice,
822 .copy = copy_opt,
823 .add = add_choice,
824 .set = choice_set,
825 .get = choice_get,
826 .equal = int_equal,
827 };
828
apply_flag(const struct m_option * opt,int * val,bstr flag)829 static int apply_flag(const struct m_option *opt, int *val, bstr flag)
830 {
831 struct m_opt_choice_alternatives *alt;
832 for (alt = opt->priv; alt->name; alt++) {
833 if (bstr_equals0(flag, alt->name)) {
834 if (*val & alt->value)
835 return M_OPT_INVALID;
836 *val |= alt->value;
837 return 0;
838 }
839 }
840 return M_OPT_UNKNOWN;
841 }
842
find_next_flag(const struct m_option * opt,int * val)843 static const char *find_next_flag(const struct m_option *opt, int *val)
844 {
845 struct m_opt_choice_alternatives *best = NULL;
846 struct m_opt_choice_alternatives *alt;
847 for (alt = opt->priv; alt->name; alt++) {
848 if (alt->value && (alt->value & (*val)) == alt->value) {
849 if (!best || av_popcount64(alt->value) > av_popcount64(best->value))
850 best = alt;
851 }
852 }
853 if (best) {
854 *val = *val & ~(unsigned)best->value;
855 return best->name;
856 }
857 *val = 0; // if there are still flags left, there's not much we can do
858 return NULL;
859 }
860
parse_flags(struct mp_log * log,const struct m_option * opt,struct bstr name,struct bstr param,void * dst)861 static int parse_flags(struct mp_log *log, const struct m_option *opt,
862 struct bstr name, struct bstr param, void *dst)
863 {
864 int value = 0;
865 while (param.len) {
866 bstr flag;
867 bstr_split_tok(param, "+", &flag, ¶m);
868 int r = apply_flag(opt, &value, flag);
869 if (r == M_OPT_UNKNOWN) {
870 mp_fatal(log, "Invalid flag for option %.*s: %.*s\n",
871 BSTR_P(name), BSTR_P(flag));
872 mp_info(log, "Valid flags are:\n");
873 struct m_opt_choice_alternatives *alt;
874 for (alt = opt->priv; alt->name; alt++)
875 mp_info(log, " %s\n", alt->name);
876 mp_info(log, "Flags can usually be combined with '+'.\n");
877 return M_OPT_INVALID;
878 } else if (r < 0) {
879 mp_fatal(log, "Option %.*s: flag '%.*s' conflicts with a previous "
880 "flag value.\n", BSTR_P(name), BSTR_P(flag));
881 return M_OPT_INVALID;
882 }
883 }
884 if (dst)
885 *(int *)dst = value;
886 return 1;
887 }
888
flags_set(const m_option_t * opt,void * dst,struct mpv_node * src)889 static int flags_set(const m_option_t *opt, void *dst, struct mpv_node *src)
890 {
891 int value = 0;
892 if (src->format != MPV_FORMAT_NODE_ARRAY)
893 return M_OPT_UNKNOWN;
894 struct mpv_node_list *srclist = src->u.list;
895 for (int n = 0; n < srclist->num; n++) {
896 if (srclist->values[n].format != MPV_FORMAT_STRING)
897 return M_OPT_INVALID;
898 if (apply_flag(opt, &value, bstr0(srclist->values[n].u.string)) < 0)
899 return M_OPT_INVALID;
900 }
901 *(int *)dst = value;
902 return 0;
903 }
904
flags_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)905 static int flags_get(const m_option_t *opt, void *ta_parent,
906 struct mpv_node *dst, void *src)
907 {
908 int value = *(int *)src;
909
910 dst->format = MPV_FORMAT_NODE_ARRAY;
911 dst->u.list = talloc_zero(ta_parent, struct mpv_node_list);
912 struct mpv_node_list *list = dst->u.list;
913 while (1) {
914 const char *flag = find_next_flag(opt, &value);
915 if (!flag)
916 break;
917
918 struct mpv_node node;
919 node.format = MPV_FORMAT_STRING;
920 node.u.string = (char *)flag;
921 MP_TARRAY_APPEND(list, list->values, list->num, node);
922 }
923
924 return 1;
925 }
926
print_flags(const m_option_t * opt,const void * val)927 static char *print_flags(const m_option_t *opt, const void *val)
928 {
929 int value = *(int *)val;
930 char *res = talloc_strdup(NULL, "");
931 while (1) {
932 const char *flag = find_next_flag(opt, &value);
933 if (!flag)
934 break;
935
936 res = talloc_asprintf_append_buffer(res, "%s%s", res[0] ? "+" : "", flag);
937 }
938 return res;
939 }
940
941 const struct m_option_type m_option_type_flags = {
942 .name = "Flags",
943 .size = sizeof(int),
944 .parse = parse_flags,
945 .print = print_flags,
946 .copy = copy_opt,
947 .set = flags_set,
948 .get = flags_get,
949 .equal = int_equal,
950 };
951
952 // Float
953
954 #undef VAL
955 #define VAL(x) (*(double *)(x))
956
clamp_double(const m_option_t * opt,void * val)957 static int clamp_double(const m_option_t *opt, void *val)
958 {
959 double v = VAL(val);
960 int r = 0;
961 if (opt->min < opt->max) {
962 if (v > opt->max) {
963 v = opt->max;
964 r = M_OPT_OUT_OF_RANGE;
965 }
966 if (v < opt->min) {
967 v = opt->min;
968 r = M_OPT_OUT_OF_RANGE;
969 }
970 }
971 // (setting max/min to INFINITY/-INFINITY is allowed)
972 if (!isfinite(v) && v != opt->max && v != opt->min) {
973 v = opt->min;
974 r = M_OPT_OUT_OF_RANGE;
975 }
976 VAL(val) = v;
977 return r;
978 }
979
parse_double(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)980 static int parse_double(struct mp_log *log, const m_option_t *opt,
981 struct bstr name, struct bstr param, void *dst)
982 {
983 if (param.len == 0)
984 return M_OPT_MISSING_PARAM;
985
986 struct bstr rest;
987 double tmp_float = bstrtod(param, &rest);
988
989 if (bstr_eatstart0(&rest, ":") || bstr_eatstart0(&rest, "/"))
990 tmp_float /= bstrtod(rest, &rest);
991
992 if ((opt->flags & M_OPT_DEFAULT_NAN) && bstr_equals0(param, "default")) {
993 tmp_float = NAN;
994 goto done;
995 }
996
997 if (rest.len) {
998 mp_err(log, "The %.*s option must be a floating point number or a "
999 "ratio (numerator[:/]denominator): %.*s\n",
1000 BSTR_P(name), BSTR_P(param));
1001 return M_OPT_INVALID;
1002 }
1003
1004 if (clamp_double(opt, &tmp_float) < 0) {
1005 mp_err(log, "The %.*s option is out of range: %.*s\n",
1006 BSTR_P(name), BSTR_P(param));
1007 return M_OPT_OUT_OF_RANGE;
1008 }
1009
1010 done:
1011 if (dst)
1012 VAL(dst) = tmp_float;
1013 return 1;
1014 }
1015
print_double(const m_option_t * opt,const void * val)1016 static char *print_double(const m_option_t *opt, const void *val)
1017 {
1018 double f = VAL(val);
1019 if (isnan(f) && (opt->flags & M_OPT_DEFAULT_NAN))
1020 return talloc_strdup(NULL, "default");
1021 return talloc_asprintf(NULL, "%f", f);
1022 }
1023
print_double_f3(const m_option_t * opt,const void * val)1024 static char *print_double_f3(const m_option_t *opt, const void *val)
1025 {
1026 double f = VAL(val);
1027 if (isnan(f))
1028 return print_double(opt, val);
1029 return talloc_asprintf(NULL, "%.3f", f);
1030 }
1031
add_double(const m_option_t * opt,void * val,double add,bool wrap)1032 static void add_double(const m_option_t *opt, void *val, double add, bool wrap)
1033 {
1034 double v = VAL(val);
1035
1036 v = v + add;
1037
1038 double min = opt->min < opt->max ? opt->min : -INFINITY;
1039 double max = opt->min < opt->max ? opt->max : +INFINITY;
1040
1041 if (v < min)
1042 v = wrap ? max : min;
1043 if (v > max)
1044 v = wrap ? min : max;
1045
1046 VAL(val) = v;
1047 }
1048
multiply_double(const m_option_t * opt,void * val,double f)1049 static void multiply_double(const m_option_t *opt, void *val, double f)
1050 {
1051 *(double *)val *= f;
1052 clamp_double(opt, val);
1053 }
1054
double_set(const m_option_t * opt,void * dst,struct mpv_node * src)1055 static int double_set(const m_option_t *opt, void *dst, struct mpv_node *src)
1056 {
1057 double val;
1058 if (src->format == MPV_FORMAT_INT64) {
1059 // Can't always be represented exactly, but don't care.
1060 val = src->u.int64;
1061 } else if (src->format == MPV_FORMAT_DOUBLE) {
1062 val = src->u.double_;
1063 } else {
1064 return M_OPT_UNKNOWN;
1065 }
1066 if (clamp_double(opt, &val) < 0)
1067 return M_OPT_OUT_OF_RANGE;
1068 *(double *)dst = val;
1069 return 1;
1070 }
1071
double_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)1072 static int double_get(const m_option_t *opt, void *ta_parent,
1073 struct mpv_node *dst, void *src)
1074 {
1075 double f = *(double *)src;
1076 if (isnan(f) && (opt->flags & M_OPT_DEFAULT_NAN)) {
1077 dst->format = MPV_FORMAT_STRING;
1078 dst->u.string = talloc_strdup(ta_parent, "default");
1079 } else {
1080 dst->format = MPV_FORMAT_DOUBLE;
1081 dst->u.double_ = f;
1082 }
1083 return 1;
1084 }
1085
double_equal(const m_option_t * opt,void * a,void * b)1086 static bool double_equal(const m_option_t *opt, void *a, void *b)
1087 {
1088 double fa = VAL(a), fb = VAL(b);
1089 if (isnan(fa) || isnan(fb))
1090 return isnan(fa) == isnan(fb);
1091 return fa == fb;
1092 }
1093
1094 const m_option_type_t m_option_type_double = {
1095 // double precision float or ratio (numerator[:/]denominator)
1096 .name = "Double",
1097 .flags = M_OPT_TYPE_USES_RANGE,
1098 .size = sizeof(double),
1099 .parse = parse_double,
1100 .print = print_double,
1101 .pretty_print = print_double_f3,
1102 .copy = copy_opt,
1103 .add = add_double,
1104 .multiply = multiply_double,
1105 .set = double_set,
1106 .get = double_get,
1107 .equal = double_equal,
1108 };
1109
1110 #undef VAL
1111 #define VAL(x) (*(float *)(x))
1112
parse_float(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)1113 static int parse_float(struct mp_log *log, const m_option_t *opt,
1114 struct bstr name, struct bstr param, void *dst)
1115 {
1116 double tmp;
1117 int r = parse_double(log, opt, name, param, &tmp);
1118 if (r == 1 && dst)
1119 VAL(dst) = tmp;
1120 return r;
1121 }
1122
print_float(const m_option_t * opt,const void * val)1123 static char *print_float(const m_option_t *opt, const void *val)
1124 {
1125 double tmp = VAL(val);
1126 return print_double(opt, &tmp);
1127 }
1128
print_float_f3(const m_option_t * opt,const void * val)1129 static char *print_float_f3(const m_option_t *opt, const void *val)
1130 {
1131 double tmp = VAL(val);
1132 return print_double_f3(opt, &tmp);
1133 }
1134
add_float(const m_option_t * opt,void * val,double add,bool wrap)1135 static void add_float(const m_option_t *opt, void *val, double add, bool wrap)
1136 {
1137 double tmp = VAL(val);
1138 add_double(opt, &tmp, add, wrap);
1139 VAL(val) = tmp;
1140 }
1141
multiply_float(const m_option_t * opt,void * val,double f)1142 static void multiply_float(const m_option_t *opt, void *val, double f)
1143 {
1144 double tmp = VAL(val);
1145 multiply_double(opt, &tmp, f);
1146 VAL(val) = tmp;
1147 }
1148
float_set(const m_option_t * opt,void * dst,struct mpv_node * src)1149 static int float_set(const m_option_t *opt, void *dst, struct mpv_node *src)
1150 {
1151 double tmp;
1152 int r = double_set(opt, &tmp, src);
1153 if (r >= 0)
1154 VAL(dst) = tmp;
1155 return r;
1156 }
1157
float_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)1158 static int float_get(const m_option_t *opt, void *ta_parent,
1159 struct mpv_node *dst, void *src)
1160 {
1161 double tmp = VAL(src);
1162 return double_get(opt, ta_parent, dst, &tmp);
1163 }
1164
float_equal(const m_option_t * opt,void * a,void * b)1165 static bool float_equal(const m_option_t *opt, void *a, void *b)
1166 {
1167 return double_equal(opt, &(double){VAL(a)}, &(double){VAL(b)});
1168 }
1169
1170 const m_option_type_t m_option_type_float = {
1171 // floating point number or ratio (numerator[:/]denominator)
1172 .name = "Float",
1173 .flags = M_OPT_TYPE_USES_RANGE,
1174 .size = sizeof(float),
1175 .parse = parse_float,
1176 .print = print_float,
1177 .pretty_print = print_float_f3,
1178 .copy = copy_opt,
1179 .add = add_float,
1180 .multiply = multiply_float,
1181 .set = float_set,
1182 .get = float_get,
1183 .equal = float_equal,
1184 };
1185
parse_float_aspect(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)1186 static int parse_float_aspect(struct mp_log *log, const m_option_t *opt,
1187 struct bstr name, struct bstr param, void *dst)
1188 {
1189 if (bstr_equals0(param, "no")) {
1190 if (dst)
1191 VAL(dst) = 0.0f;
1192 return 1;
1193 }
1194 return parse_float(log, opt, name, param, dst);
1195 }
1196
1197 const m_option_type_t m_option_type_aspect = {
1198 .name = "Aspect",
1199 .size = sizeof(float),
1200 .flags = M_OPT_TYPE_CHOICE | M_OPT_TYPE_USES_RANGE,
1201 .parse = parse_float_aspect,
1202 .print = print_float,
1203 .pretty_print = print_float_f3,
1204 .copy = copy_opt,
1205 .add = add_float,
1206 .multiply = multiply_float,
1207 .set = float_set,
1208 .get = float_get,
1209 .equal = float_equal,
1210 };
1211
1212 ///////////// String
1213
1214 #undef VAL
1215 #define VAL(x) (*(char **)(x))
1216
parse_str(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)1217 static int parse_str(struct mp_log *log, const m_option_t *opt,
1218 struct bstr name, struct bstr param, void *dst)
1219 {
1220 if (dst) {
1221 talloc_free(VAL(dst));
1222 VAL(dst) = bstrdup0(NULL, param);
1223 }
1224
1225 return 0;
1226 }
1227
print_str(const m_option_t * opt,const void * val)1228 static char *print_str(const m_option_t *opt, const void *val)
1229 {
1230 return talloc_strdup(NULL, VAL(val) ? VAL(val) : "");
1231 }
1232
copy_str(const m_option_t * opt,void * dst,const void * src)1233 static void copy_str(const m_option_t *opt, void *dst, const void *src)
1234 {
1235 if (dst && src) {
1236 talloc_free(VAL(dst));
1237 VAL(dst) = talloc_strdup(NULL, VAL(src));
1238 }
1239 }
1240
str_set(const m_option_t * opt,void * dst,struct mpv_node * src)1241 static int str_set(const m_option_t *opt, void *dst, struct mpv_node *src)
1242 {
1243 if (src->format != MPV_FORMAT_STRING)
1244 return M_OPT_UNKNOWN;
1245 char *s = src->u.string;
1246 int r = s ? 0 : M_OPT_INVALID;
1247 if (r >= 0)
1248 copy_str(opt, dst, &s);
1249 return r;
1250 }
1251
str_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)1252 static int str_get(const m_option_t *opt, void *ta_parent,
1253 struct mpv_node *dst, void *src)
1254 {
1255 dst->format = MPV_FORMAT_STRING;
1256 dst->u.string = talloc_strdup(ta_parent, VAL(src) ? VAL(src) : "");
1257 return 1;
1258 }
1259
str_equal(const m_option_t * opt,void * a,void * b)1260 static bool str_equal(const m_option_t *opt, void *a, void *b)
1261 {
1262 return bstr_equals(bstr0(VAL(a)), bstr0(VAL(b)));
1263 }
1264
free_str(void * src)1265 static void free_str(void *src)
1266 {
1267 if (src && VAL(src)) {
1268 talloc_free(VAL(src));
1269 VAL(src) = NULL;
1270 }
1271 }
1272
1273 const m_option_type_t m_option_type_string = {
1274 .name = "String",
1275 .size = sizeof(char *),
1276 .parse = parse_str,
1277 .print = print_str,
1278 .copy = copy_str,
1279 .free = free_str,
1280 .set = str_set,
1281 .get = str_get,
1282 .equal = str_equal,
1283 };
1284
1285 //////////// String list
1286
1287 #undef VAL
1288 #define VAL(x) (*(char ***)(x))
1289
1290 #define OP_NONE 0
1291 #define OP_ADD 1
1292 #define OP_PRE 2
1293 #define OP_DEL 3
1294 #define OP_CLR 4
1295 #define OP_TOGGLE 5
1296 #define OP_APPEND 6
1297 #define OP_REMOVE 7
1298
free_str_list(void * dst)1299 static void free_str_list(void *dst)
1300 {
1301 char **d;
1302 int i;
1303
1304 if (!dst || !VAL(dst))
1305 return;
1306 d = VAL(dst);
1307
1308 for (i = 0; d[i] != NULL; i++)
1309 talloc_free(d[i]);
1310 talloc_free(d);
1311 VAL(dst) = NULL;
1312 }
1313
str_list_add(char ** add,int n,void * dst,int pre)1314 static int str_list_add(char **add, int n, void *dst, int pre)
1315 {
1316 char **lst = VAL(dst);
1317
1318 int ln;
1319 for (ln = 0; lst && lst[ln]; ln++)
1320 /**/;
1321
1322 lst = talloc_realloc(NULL, lst, char *, n + ln + 1);
1323
1324 if (pre) {
1325 memmove(&lst[n], lst, ln * sizeof(char *));
1326 memcpy(lst, add, n * sizeof(char *));
1327 } else
1328 memcpy(&lst[ln], add, n * sizeof(char *));
1329 // (re-)add NULL-termination
1330 lst[ln + n] = NULL;
1331
1332 talloc_free(add);
1333
1334 VAL(dst) = lst;
1335
1336 return 1;
1337 }
1338
str_list_del(struct mp_log * log,char ** del,int n,void * dst)1339 static int str_list_del(struct mp_log *log, char **del, int n, void *dst)
1340 {
1341 char **lst, *ep;
1342 int i, ln, s;
1343 long idx;
1344
1345 lst = VAL(dst);
1346
1347 for (ln = 0; lst && lst[ln]; ln++)
1348 /**/;
1349 s = ln;
1350
1351 for (i = 0; del[i] != NULL; i++) {
1352 idx = strtol(del[i], &ep, 0);
1353 if (*ep) {
1354 mp_err(log, "Invalid index: %s\n", del[i]);
1355 talloc_free(del[i]);
1356 continue;
1357 }
1358 talloc_free(del[i]);
1359 if (idx < 0 || idx >= ln) {
1360 mp_err(log, "Index %ld is out of range.\n", idx);
1361 continue;
1362 } else if (!lst[idx])
1363 continue;
1364 talloc_free(lst[idx]);
1365 lst[idx] = NULL;
1366 s--;
1367 }
1368 talloc_free(del);
1369
1370 if (s == 0) {
1371 talloc_free(lst);
1372 VAL(dst) = NULL;
1373 return 1;
1374 }
1375
1376 // Don't bother shrinking the list allocation
1377 for (i = 0, n = 0; i < ln; i++) {
1378 if (!lst[i])
1379 continue;
1380 lst[n] = lst[i];
1381 n++;
1382 }
1383 lst[s] = NULL;
1384
1385 return 1;
1386 }
1387
get_nextsep(struct bstr * ptr,char sep,bool modify)1388 static struct bstr get_nextsep(struct bstr *ptr, char sep, bool modify)
1389 {
1390 struct bstr str = *ptr;
1391 struct bstr orig = str;
1392 for (;;) {
1393 int idx = sep ? bstrchr(str, sep) : -1;
1394 if (idx > 0 && str.start[idx - 1] == '\\') {
1395 if (modify) {
1396 memmove(str.start + idx - 1, str.start + idx, str.len - idx);
1397 str.len--;
1398 str = bstr_cut(str, idx);
1399 } else
1400 str = bstr_cut(str, idx + 1);
1401 } else {
1402 str = bstr_cut(str, idx < 0 ? str.len : idx);
1403 break;
1404 }
1405 }
1406 *ptr = str;
1407 return bstr_splice(orig, 0, str.start - orig.start);
1408 }
1409
find_list_bstr(char ** list,bstr item)1410 static int find_list_bstr(char **list, bstr item)
1411 {
1412 for (int n = 0; list && list[n]; n++) {
1413 if (bstr_equals0(item, list[n]))
1414 return n;
1415 }
1416 return -1;
1417 }
1418
parse_str_list_impl(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst,int default_op)1419 static int parse_str_list_impl(struct mp_log *log, const m_option_t *opt,
1420 struct bstr name, struct bstr param, void *dst,
1421 int default_op)
1422 {
1423 char **res;
1424 int op = default_op;
1425 bool multi = true;
1426
1427 if (bstr_endswith0(name, "-add")) {
1428 op = OP_ADD;
1429 } else if (bstr_endswith0(name, "-append")) {
1430 op = OP_ADD;
1431 multi = false;
1432 } else if (bstr_endswith0(name, "-pre")) {
1433 op = OP_PRE;
1434 } else if (bstr_endswith0(name, "-del")) {
1435 op = OP_DEL;
1436 mp_warn(log, "Option %.*s: -del is deprecated! "
1437 "Use -remove (removes by content instead of by index).\n",
1438 BSTR_P(name));
1439 } else if (bstr_endswith0(name, "-clr")) {
1440 op = OP_CLR;
1441 } else if (bstr_endswith0(name, "-set")) {
1442 op = OP_NONE;
1443 } else if (bstr_endswith0(name, "-toggle")) {
1444 op = OP_TOGGLE;
1445 } else if (bstr_endswith0(name, "-remove")) {
1446 op = OP_REMOVE;
1447 }
1448
1449 if (op == OP_TOGGLE || op == OP_REMOVE) {
1450 if (dst) {
1451 char **list = VAL(dst);
1452 int index = find_list_bstr(list, param);
1453 if (index >= 0) {
1454 char *old = list[index];
1455 for (int n = index; list[n]; n++)
1456 list[n] = list[n + 1];
1457 talloc_free(old);
1458 return 1;
1459 }
1460 }
1461 if (op == OP_REMOVE)
1462 return 1; // ignore if not found
1463 op = OP_ADD;
1464 multi = false;
1465 }
1466
1467 // Clear the list ??
1468 if (op == OP_CLR) {
1469 if (dst)
1470 free_str_list(dst);
1471 return 0;
1472 }
1473
1474 // All other ops need a param
1475 if (param.len == 0 && op != OP_NONE)
1476 return M_OPT_MISSING_PARAM;
1477
1478 char separator = opt->priv ? *(char *)opt->priv : OPTION_LIST_SEPARATOR;
1479 if (!multi)
1480 separator = 0; // specially handled
1481 int n = 0;
1482 struct bstr str = param;
1483 while (str.len) {
1484 get_nextsep(&str, separator, 0);
1485 str = bstr_cut(str, 1);
1486 n++;
1487 }
1488 if (n == 0 && op != OP_NONE)
1489 return M_OPT_INVALID;
1490
1491 if (!dst)
1492 return 1;
1493
1494 res = talloc_array(NULL, char *, n + 2);
1495 str = bstrdup(NULL, param);
1496 char *ptr = str.start;
1497 n = 0;
1498
1499 while (1) {
1500 struct bstr el = get_nextsep(&str, separator, 1);
1501 res[n] = bstrdup0(NULL, el);
1502 n++;
1503 if (!str.len)
1504 break;
1505 str = bstr_cut(str, 1);
1506 }
1507 res[n] = NULL;
1508 talloc_free(ptr);
1509
1510 if (op != OP_NONE && n > 1) {
1511 mp_warn(log, "Passing multiple arguments to %.*s is deprecated!\n",
1512 BSTR_P(name));
1513 }
1514
1515 switch (op) {
1516 case OP_ADD:
1517 return str_list_add(res, n, dst, 0);
1518 case OP_PRE:
1519 return str_list_add(res, n, dst, 1);
1520 case OP_DEL:
1521 return str_list_del(log, res, n, dst);
1522 }
1523
1524 if (VAL(dst))
1525 free_str_list(dst);
1526 VAL(dst) = res;
1527
1528 if (!res[0])
1529 free_str_list(dst);
1530
1531 return 1;
1532 }
1533
copy_str_list(const m_option_t * opt,void * dst,const void * src)1534 static void copy_str_list(const m_option_t *opt, void *dst, const void *src)
1535 {
1536 int n;
1537 char **d, **s;
1538
1539 if (!(dst && src))
1540 return;
1541 s = VAL(src);
1542
1543 if (VAL(dst))
1544 free_str_list(dst);
1545
1546 if (!s) {
1547 VAL(dst) = NULL;
1548 return;
1549 }
1550
1551 for (n = 0; s[n] != NULL; n++)
1552 /* NOTHING */;
1553 d = talloc_array(NULL, char *, n + 1);
1554 for (; n >= 0; n--)
1555 d[n] = talloc_strdup(NULL, s[n]);
1556
1557 VAL(dst) = d;
1558 }
1559
print_str_list(const m_option_t * opt,const void * src)1560 static char *print_str_list(const m_option_t *opt, const void *src)
1561 {
1562 char **lst = NULL;
1563 char *ret = NULL;
1564
1565 if (!(src && VAL(src)))
1566 return talloc_strdup(NULL, "");
1567 lst = VAL(src);
1568
1569 for (int i = 0; lst[i]; i++) {
1570 if (ret)
1571 ret = talloc_strdup_append_buffer(ret, ",");
1572 ret = talloc_strdup_append_buffer(ret, lst[i]);
1573 }
1574 return ret;
1575 }
1576
str_list_set(const m_option_t * opt,void * dst,struct mpv_node * src)1577 static int str_list_set(const m_option_t *opt, void *dst, struct mpv_node *src)
1578 {
1579 if (src->format != MPV_FORMAT_NODE_ARRAY)
1580 return M_OPT_UNKNOWN;
1581 struct mpv_node_list *srclist = src->u.list;
1582 for (int n = 0; n < srclist->num; n++) {
1583 if (srclist->values[n].format != MPV_FORMAT_STRING)
1584 return M_OPT_INVALID;
1585 }
1586 free_str_list(dst);
1587 if (srclist->num > 0) {
1588 VAL(dst) = talloc_array(NULL, char*, srclist->num + 1);
1589 for (int n = 0; n < srclist->num; n++)
1590 VAL(dst)[n] = talloc_strdup(NULL, srclist->values[n].u.string);
1591 VAL(dst)[srclist->num] = NULL;
1592 }
1593 return 1;
1594 }
1595
str_list_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)1596 static int str_list_get(const m_option_t *opt, void *ta_parent,
1597 struct mpv_node *dst, void *src)
1598 {
1599 dst->format = MPV_FORMAT_NODE_ARRAY;
1600 dst->u.list = talloc_zero(ta_parent, struct mpv_node_list);
1601 struct mpv_node_list *list = dst->u.list;
1602 for (int n = 0; VAL(src) && VAL(src)[n]; n++) {
1603 struct mpv_node node;
1604 node.format = MPV_FORMAT_STRING;
1605 node.u.string = talloc_strdup(list, VAL(src)[n]);
1606 MP_TARRAY_APPEND(list, list->values, list->num, node);
1607 }
1608 return 1;
1609 }
1610
parse_str_list(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)1611 static int parse_str_list(struct mp_log *log, const m_option_t *opt,
1612 struct bstr name, struct bstr param, void *dst)
1613 {
1614 return parse_str_list_impl(log, opt, name, param, dst, OP_NONE);
1615 }
1616
str_list_equal(const m_option_t * opt,void * a,void * b)1617 static bool str_list_equal(const m_option_t *opt, void *a, void *b)
1618 {
1619 char **la = VAL(a);
1620 char **lb = VAL(b);
1621
1622 bool a_empty = !la || !la[0];
1623 bool b_empty = !lb || !lb[0];
1624 if (a_empty || b_empty)
1625 return a_empty == b_empty;
1626
1627 for (int n = 0; la[n] || lb[n]; n++) {
1628 if (!la[n] || !lb[n])
1629 return false;
1630 if (strcmp(la[n], lb[n]) != 0)
1631 return false;
1632 }
1633
1634 return true;
1635 }
1636
1637 const m_option_type_t m_option_type_string_list = {
1638 .name = "String list",
1639 .size = sizeof(char **),
1640 .parse = parse_str_list,
1641 .print = print_str_list,
1642 .copy = copy_str_list,
1643 .free = free_str_list,
1644 .get = str_list_get,
1645 .set = str_list_set,
1646 .equal = str_list_equal,
1647 .actions = (const struct m_option_action[]){
1648 {"add"},
1649 {"append"},
1650 {"clr", M_OPT_TYPE_OPTIONAL_PARAM},
1651 {"del"},
1652 {"pre"},
1653 {"set"},
1654 {"toggle"},
1655 {"remove"},
1656 {0}
1657 },
1658 };
1659
1660 static int read_subparam(struct mp_log *log, bstr optname, char *termset,
1661 bstr *str, bstr *out_subparam);
1662
keyvalue_list_find_key(char ** lst,bstr str)1663 static int keyvalue_list_find_key(char **lst, bstr str)
1664 {
1665 for (int n = 0; lst && lst[n] && lst[n + 1]; n += 2) {
1666 if (bstr_equals0(str, lst[n]))
1667 return n / 2;
1668 }
1669 return -1;
1670 }
1671
keyvalue_list_del_key(char ** lst,int index)1672 static void keyvalue_list_del_key(char **lst, int index)
1673 {
1674 int count = 0;
1675 for (int n = 0; lst && lst[n]; n++)
1676 count++;
1677 assert(index * 2 + 1 < count);
1678 count += 1; // terminating item
1679 talloc_free(lst[index * 2 + 0]);
1680 talloc_free(lst[index * 2 + 1]);
1681 MP_TARRAY_REMOVE_AT(lst, count, index * 2 + 1);
1682 MP_TARRAY_REMOVE_AT(lst, count, index * 2 + 0);
1683 }
1684
parse_keyvalue_list(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)1685 static int parse_keyvalue_list(struct mp_log *log, const m_option_t *opt,
1686 struct bstr name, struct bstr param, void *dst)
1687 {
1688 char **lst = NULL;
1689 int num = 0;
1690 int r = 0;
1691 bool append = false;
1692 bool full_value = false;
1693
1694 if ((opt->flags & M_OPT_HAVE_HELP) && bstr_equals0(param, "help"))
1695 param = bstr0("help=");
1696
1697 if (bstr_endswith0(name, "-add")) {
1698 append = true;
1699 } else if (bstr_endswith0(name, "-append")) {
1700 append = full_value = true;
1701 } else if (bstr_endswith0(name, "-remove")) {
1702 lst = dst ? VAL(dst) : NULL;
1703 int index = dst ? keyvalue_list_find_key(lst, param) : -1;
1704 if (index >= 0) {
1705 keyvalue_list_del_key(lst, index);
1706 VAL(dst) = lst;
1707 }
1708 return 1;
1709 }
1710
1711 if (append && dst) {
1712 lst = VAL(dst);
1713 for (int n = 0; lst && lst[n]; n++)
1714 num++;
1715 }
1716
1717 while (param.len) {
1718 bstr key, val;
1719 r = read_subparam(log, name, "=", ¶m, &key);
1720 if (r < 0)
1721 break;
1722 if (!bstr_eatstart0(¶m, "=")) {
1723 mp_err(log, "Expected '=' and a value.\n");
1724 r = M_OPT_INVALID;
1725 break;
1726 }
1727 if (full_value) {
1728 val = param;
1729 param.len = 0;
1730 } else {
1731 r = read_subparam(log, name, ",", ¶m, &val);
1732 if (r < 0)
1733 break;
1734 }
1735 if (dst) {
1736 int index = keyvalue_list_find_key(lst, key);
1737 if (index >= 0) {
1738 keyvalue_list_del_key(lst, index);
1739 num -= 2;
1740 }
1741 MP_TARRAY_APPEND(NULL, lst, num, bstrto0(NULL, key));
1742 MP_TARRAY_APPEND(NULL, lst, num, bstrto0(NULL, val));
1743 MP_TARRAY_APPEND(NULL, lst, num, NULL);
1744 num -= 1;
1745 }
1746
1747 if (!bstr_eatstart0(¶m, ",") && !bstr_eatstart0(¶m, ":"))
1748 break;
1749
1750 if (append) {
1751 mp_warn(log, "Passing more than 1 argument to %.*s is deprecated!\n",
1752 BSTR_P(name));
1753 }
1754 }
1755
1756 if (param.len) {
1757 mp_err(log, "Unparseable garbage at end of option value: '%.*s'\n",
1758 BSTR_P(param));
1759 r = M_OPT_INVALID;
1760 }
1761
1762 if (dst) {
1763 if (!append)
1764 free_str_list(dst);
1765 VAL(dst) = lst;
1766 if (r < 0)
1767 free_str_list(dst);
1768 } else {
1769 free_str_list(&lst);
1770 }
1771 return r;
1772 }
1773
print_keyvalue_list(const m_option_t * opt,const void * src)1774 static char *print_keyvalue_list(const m_option_t *opt, const void *src)
1775 {
1776 char **lst = VAL(src);
1777 char *ret = talloc_strdup(NULL, "");
1778 for (int n = 0; lst && lst[n] && lst[n + 1]; n += 2) {
1779 if (ret[0])
1780 ret = talloc_strdup_append(ret, ",");
1781 ret = talloc_asprintf_append(ret, "%s=%s", lst[n], lst[n + 1]);
1782 }
1783 return ret;
1784 }
1785
keyvalue_list_set(const m_option_t * opt,void * dst,struct mpv_node * src)1786 static int keyvalue_list_set(const m_option_t *opt, void *dst,
1787 struct mpv_node *src)
1788 {
1789 if (src->format != MPV_FORMAT_NODE_MAP)
1790 return M_OPT_UNKNOWN;
1791 struct mpv_node_list *srclist = src->u.list;
1792 for (int n = 0; n < srclist->num; n++) {
1793 if (srclist->values[n].format != MPV_FORMAT_STRING)
1794 return M_OPT_INVALID;
1795 }
1796 free_str_list(dst);
1797 if (srclist->num > 0) {
1798 VAL(dst) = talloc_array(NULL, char*, (srclist->num + 1) * 2);
1799 for (int n = 0; n < srclist->num; n++) {
1800 VAL(dst)[n * 2 + 0] = talloc_strdup(NULL, srclist->keys[n]);
1801 VAL(dst)[n * 2 + 1] = talloc_strdup(NULL, srclist->values[n].u.string);
1802 }
1803 VAL(dst)[srclist->num * 2 + 0] = NULL;
1804 VAL(dst)[srclist->num * 2 + 1] = NULL;
1805 }
1806 return 1;
1807 }
1808
keyvalue_list_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)1809 static int keyvalue_list_get(const m_option_t *opt, void *ta_parent,
1810 struct mpv_node *dst, void *src)
1811 {
1812 dst->format = MPV_FORMAT_NODE_MAP;
1813 dst->u.list = talloc_zero(ta_parent, struct mpv_node_list);
1814 struct mpv_node_list *list = dst->u.list;
1815 for (int n = 0; VAL(src) && VAL(src)[n * 2 + 0]; n++) {
1816 MP_TARRAY_GROW(list, list->values, list->num);
1817 MP_TARRAY_GROW(list, list->keys, list->num);
1818 list->keys[list->num] = talloc_strdup(list, VAL(src)[n * 2 + 0]);
1819 list->values[list->num] = (struct mpv_node){
1820 .format = MPV_FORMAT_STRING,
1821 .u.string = talloc_strdup(list, VAL(src)[n * 2 + 1]),
1822 };
1823 list->num++;
1824 }
1825 return 1;
1826 }
1827
1828 const m_option_type_t m_option_type_keyvalue_list = {
1829 .name = "Key/value list",
1830 .size = sizeof(char **),
1831 .parse = parse_keyvalue_list,
1832 .print = print_keyvalue_list,
1833 .copy = copy_str_list,
1834 .free = free_str_list,
1835 .get = keyvalue_list_get,
1836 .set = keyvalue_list_set,
1837 .equal = str_list_equal,
1838 .actions = (const struct m_option_action[]){
1839 {"add"},
1840 {"append"},
1841 {"set"},
1842 {"remove"},
1843 {0}
1844 },
1845 };
1846
1847
1848 #undef VAL
1849 #define VAL(x) (*(char **)(x))
1850
check_msg_levels(struct mp_log * log,char ** list)1851 static int check_msg_levels(struct mp_log *log, char **list)
1852 {
1853 for (int n = 0; list && list[n * 2 + 0]; n++) {
1854 char *level = list[n * 2 + 1];
1855 if (mp_msg_find_level(level) < 0 && strcmp(level, "no") != 0) {
1856 mp_err(log, "Invalid message level '%s'\n", level);
1857 return M_OPT_INVALID;
1858 }
1859 }
1860 return 1;
1861 }
1862
parse_msglevels(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)1863 static int parse_msglevels(struct mp_log *log, const m_option_t *opt,
1864 struct bstr name, struct bstr param, void *dst)
1865 {
1866 if (bstr_equals0(param, "help")) {
1867 mp_info(log, "Syntax:\n\n --msg-level=module1=level,module2=level,...\n\n"
1868 "'module' is output prefix as shown with -v, or a prefix\n"
1869 "of it. level is one of:\n\n"
1870 " fatal error warn info status v debug trace\n\n"
1871 "The level specifies the minimum log level a message\n"
1872 "must have to be printed.\n"
1873 "The special module name 'all' affects all modules.\n");
1874 return M_OPT_EXIT;
1875 }
1876
1877 char **dst_copy = NULL;
1878 int r = m_option_type_keyvalue_list.parse(log, opt, name, param, &dst_copy);
1879 if (r >= 0)
1880 r = check_msg_levels(log, dst_copy);
1881
1882 if (r >= 0)
1883 m_option_type_keyvalue_list.copy(opt, dst, &dst_copy);
1884 m_option_type_keyvalue_list.free(&dst_copy);
1885 return r;
1886 }
1887
set_msglevels(const m_option_t * opt,void * dst,struct mpv_node * src)1888 static int set_msglevels(const m_option_t *opt, void *dst,
1889 struct mpv_node *src)
1890 {
1891 char **dst_copy = NULL;
1892 int r = m_option_type_keyvalue_list.set(opt, &dst_copy, src);
1893 if (r >= 0)
1894 r = check_msg_levels(mp_null_log, dst_copy);
1895
1896 if (r >= 0)
1897 m_option_type_keyvalue_list.copy(opt, dst, &dst_copy);
1898 m_option_type_keyvalue_list.free(&dst_copy);
1899 return r;
1900 }
1901
1902 const m_option_type_t m_option_type_msglevels = {
1903 .name = "Output verbosity levels",
1904 .size = sizeof(char **),
1905 .parse = parse_msglevels,
1906 .print = print_keyvalue_list,
1907 .copy = copy_str_list,
1908 .free = free_str_list,
1909 .get = keyvalue_list_get,
1910 .set = set_msglevels,
1911 .equal = str_list_equal,
1912 };
1913
parse_print(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)1914 static int parse_print(struct mp_log *log, const m_option_t *opt,
1915 struct bstr name, struct bstr param, void *dst)
1916 {
1917 ((m_opt_print_fn) opt->priv)(log);
1918 return M_OPT_EXIT;
1919 }
1920
1921 const m_option_type_t m_option_type_print_fn = {
1922 .name = "Print",
1923 .flags = M_OPT_TYPE_OPTIONAL_PARAM,
1924 .parse = parse_print,
1925 };
1926
parse_dummy_flag(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)1927 static int parse_dummy_flag(struct mp_log *log, const m_option_t *opt,
1928 struct bstr name, struct bstr param, void *dst)
1929 {
1930 if (param.len) {
1931 mp_err(log, "Invalid parameter for %.*s flag: %.*s\n",
1932 BSTR_P(name), BSTR_P(param));
1933 return M_OPT_DISALLOW_PARAM;
1934 }
1935 return 0;
1936 }
1937
1938 const m_option_type_t m_option_type_dummy_flag = {
1939 // can only be activated
1940 .name = "Flag",
1941 .flags = M_OPT_TYPE_OPTIONAL_PARAM,
1942 .parse = parse_dummy_flag,
1943 };
1944
1945 #undef VAL
1946
1947 // Read s sub-option name, or a positional sub-opt value.
1948 // termset is a string containing the set of chars that terminate an option.
1949 // Return 0 on succes, M_OPT_ error code otherwise.
1950 // optname is for error reporting.
read_subparam(struct mp_log * log,bstr optname,char * termset,bstr * str,bstr * out_subparam)1951 static int read_subparam(struct mp_log *log, bstr optname, char *termset,
1952 bstr *str, bstr *out_subparam)
1953 {
1954 bstr p = *str;
1955 bstr subparam = {0};
1956
1957 if (bstr_eatstart0(&p, "\"")) {
1958 int optlen = bstrcspn(p, "\"");
1959 subparam = bstr_splice(p, 0, optlen);
1960 p = bstr_cut(p, optlen);
1961 if (!bstr_startswith0(p, "\"")) {
1962 mp_err(log, "Terminating '\"' missing for '%.*s'\n",
1963 BSTR_P(optname));
1964 return M_OPT_INVALID;
1965 }
1966 p = bstr_cut(p, 1);
1967 } else if (bstr_eatstart0(&p, "[")) {
1968 bstr s = p;
1969 int balance = 1;
1970 while (p.len && balance > 0) {
1971 if (p.start[0] == '[') {
1972 balance++;
1973 } else if (p.start[0] == ']') {
1974 balance--;
1975 }
1976 p = bstr_cut(p, 1);
1977 }
1978 if (balance != 0) {
1979 mp_err(log, "Terminating ']' missing for '%.*s'\n",
1980 BSTR_P(optname));
1981 return M_OPT_INVALID;
1982 }
1983 subparam = bstr_splice(s, 0, s.len - p.len - 1);
1984 } else if (bstr_eatstart0(&p, "%")) {
1985 int optlen = bstrtoll(p, &p, 0);
1986 if (!bstr_startswith0(p, "%") || (optlen > p.len - 1)) {
1987 mp_err(log, "Invalid length %d for '%.*s'\n",
1988 optlen, BSTR_P(optname));
1989 return M_OPT_INVALID;
1990 }
1991 subparam = bstr_splice(p, 1, optlen + 1);
1992 p = bstr_cut(p, optlen + 1);
1993 } else {
1994 // Skip until the next character that could possibly be a meta
1995 // character in option parsing.
1996 int optlen = bstrcspn(p, termset);
1997 subparam = bstr_splice(p, 0, optlen);
1998 p = bstr_cut(p, optlen);
1999 }
2000
2001 *str = p;
2002 *out_subparam = subparam;
2003 return 0;
2004 }
2005
2006 // Return 0 on success, otherwise error code
2007 // On success, set *out_name and *out_val, and advance *str
2008 // out_val.start is NULL if there was no parameter.
2009 // optname is for error reporting.
split_subconf(struct mp_log * log,bstr optname,bstr * str,bstr * out_name,bstr * out_val)2010 static int split_subconf(struct mp_log *log, bstr optname, bstr *str,
2011 bstr *out_name, bstr *out_val)
2012 {
2013 bstr p = *str;
2014 bstr subparam = {0};
2015 bstr subopt;
2016 int r = read_subparam(log, optname, ":=,\\%\"'[]", &p, &subopt);
2017 if (r < 0)
2018 return r;
2019 if (bstr_eatstart0(&p, "=")) {
2020 r = read_subparam(log, subopt, ":=,\\%\"'[]", &p, &subparam);
2021 if (r < 0)
2022 return r;
2023 }
2024 *str = p;
2025 *out_name = subopt;
2026 *out_val = subparam;
2027 return 0;
2028 }
2029
2030 #undef VAL
2031
2032 // Split the string on the given split character.
2033 // out_arr is at least max entries long.
2034 // Return number of out_arr entries filled.
split_char(bstr str,unsigned char split,int max,bstr * out_arr)2035 static int split_char(bstr str, unsigned char split, int max, bstr *out_arr)
2036 {
2037 if (max < 1)
2038 return 0;
2039
2040 int count = 0;
2041 while (1) {
2042 int next = bstrchr(str, split);
2043 if (next >= 0 && max - count > 1) {
2044 out_arr[count++] = bstr_splice(str, 0, next);
2045 str = bstr_cut(str, next + 1);
2046 } else {
2047 out_arr[count++] = str;
2048 break;
2049 }
2050 }
2051 return count;
2052 }
2053
parse_color(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2054 static int parse_color(struct mp_log *log, const m_option_t *opt,
2055 struct bstr name, struct bstr param, void *dst)
2056 {
2057 if (param.len == 0)
2058 return M_OPT_MISSING_PARAM;
2059
2060 bool is_help = bstr_equals0(param, "help");
2061 if (is_help)
2062 goto exit;
2063
2064 bstr val = param;
2065 struct m_color color = {0};
2066
2067 if (bstr_eatstart0(&val, "#")) {
2068 // #[AA]RRGGBB
2069 if (val.len != 6 && val.len != 8)
2070 goto exit;
2071 bool has_alpha = val.len == 8;
2072 uint32_t c = bstrtoll(val, &val, 16);
2073 if (val.len)
2074 goto exit;
2075 color = (struct m_color) {
2076 (c >> 16) & 0xFF,
2077 (c >> 8) & 0xFF,
2078 c & 0xFF,
2079 has_alpha ? (c >> 24) & 0xFF : 0xFF,
2080 };
2081 } else {
2082 bstr comp_str[5];
2083 int num = split_char(param, '/', 5, comp_str);
2084 if (num < 1 || num > 4)
2085 goto exit;
2086 double comp[4] = {0, 0, 0, 1};
2087 for (int n = 0; n < num; n++) {
2088 bstr rest;
2089 double d = bstrtod(comp_str[n], &rest);
2090 if (rest.len || !comp_str[n].len || d < 0 || d > 1 || !isfinite(d))
2091 goto exit;
2092 comp[n] = d;
2093 }
2094 if (num == 2)
2095 comp[3] = comp[1];
2096 if (num < 3)
2097 comp[2] = comp[1] = comp[0];
2098 color = (struct m_color) { comp[0] * 0xFF, comp[1] * 0xFF,
2099 comp[2] * 0xFF, comp[3] * 0xFF };
2100 }
2101
2102 if (dst)
2103 *((struct m_color *)dst) = color;
2104
2105 return 1;
2106
2107 exit:
2108 if (!is_help) {
2109 mp_err(log, "Option %.*s: invalid color: '%.*s'\n",
2110 BSTR_P(name), BSTR_P(param));
2111 }
2112 mp_info(log, "Valid colors must be in the form #RRGGBB or #AARRGGBB (in hex)\n"
2113 "or in the form 'r/g/b/a', where each component is a value in the\n"
2114 "range 0.0-1.0. (Also allowed: 'gray', 'gray/a', 'r/g/b').\n");
2115 return is_help ? M_OPT_EXIT : M_OPT_INVALID;
2116 }
2117
print_color(const m_option_t * opt,const void * val)2118 static char *print_color(const m_option_t *opt, const void *val)
2119 {
2120 const struct m_color *c = val;
2121 return talloc_asprintf(NULL, "#%02X%02X%02X%02X", c->a, c->r, c->g, c->b);
2122 }
2123
color_equal(const m_option_t * opt,void * a,void * b)2124 static bool color_equal(const m_option_t *opt, void *a, void *b)
2125 {
2126 struct m_color *ca = a;
2127 struct m_color *cb = b;
2128 return ca->a == cb->a && ca->r == cb->r && ca->g == cb->g && ca->b == cb->b;
2129 }
2130
2131 const m_option_type_t m_option_type_color = {
2132 .name = "Color",
2133 .size = sizeof(struct m_color),
2134 .parse = parse_color,
2135 .print = print_color,
2136 .copy = copy_opt,
2137 .equal = color_equal,
2138 };
2139
2140
2141 // Parse a >=0 number starting at s. Set s to the string following the number.
2142 // If the number ends with '%', eat that and set *out_per to true, but only
2143 // if the number is between 0-100; if not, don't eat anything, even the number.
eat_num_per(bstr * s,int * out_num,bool * out_per)2144 static bool eat_num_per(bstr *s, int *out_num, bool *out_per)
2145 {
2146 bstr rest;
2147 long long v = bstrtoll(*s, &rest, 10);
2148 if (s->len == rest.len || v < INT_MIN || v > INT_MAX)
2149 return false;
2150 *out_num = v;
2151 *out_per = false;
2152 *s = rest;
2153 if (bstr_eatstart0(&rest, "%") && v >= 0 && v <= 100) {
2154 *out_per = true;
2155 *s = rest;
2156 }
2157 return true;
2158 }
2159
parse_geometry_str(struct m_geometry * gm,bstr s)2160 static bool parse_geometry_str(struct m_geometry *gm, bstr s)
2161 {
2162 *gm = (struct m_geometry) { .x = INT_MIN, .y = INT_MIN };
2163 if (s.len == 0)
2164 return true;
2165 // Approximate grammar:
2166 // [[W][xH]][{+-}X{+-}Y][/WS] | [X:Y]
2167 // (meaning: [optional] {one character of} one|alternative)
2168 // Every number can be followed by '%'
2169 int num;
2170 bool per;
2171
2172 #define READ_NUM(F, F_PER) do { \
2173 if (!eat_num_per(&s, &num, &per)) \
2174 goto error; \
2175 gm->F = num; \
2176 gm->F_PER = per; \
2177 } while(0)
2178
2179 #define READ_SIGN(F) do { \
2180 if (bstr_eatstart0(&s, "+")) { \
2181 gm->F = false; \
2182 } else if (bstr_eatstart0(&s, "-")) {\
2183 gm->F = true; \
2184 } else goto error; \
2185 } while(0)
2186
2187 if (bstrchr(s, ':') < 0) {
2188 gm->wh_valid = true;
2189 if (!bstr_startswith0(s, "+") && !bstr_startswith0(s, "-")) {
2190 if (!bstr_startswith0(s, "x"))
2191 READ_NUM(w, w_per);
2192 if (bstr_eatstart0(&s, "x"))
2193 READ_NUM(h, h_per);
2194 }
2195 if (s.len > 0) {
2196 gm->xy_valid = true;
2197 READ_SIGN(x_sign);
2198 READ_NUM(x, x_per);
2199 READ_SIGN(y_sign);
2200 READ_NUM(y, y_per);
2201 }
2202 if (bstr_eatstart0(&s, "/")) {
2203 bstr rest;
2204 long long v = bstrtoll(s, &rest, 10);
2205 if (s.len == rest.len || v < 1 || v > INT_MAX)
2206 goto error;
2207 s = rest;
2208 gm->ws = v;
2209 }
2210 } else {
2211 gm->xy_valid = true;
2212 READ_NUM(x, x_per);
2213 if (!bstr_eatstart0(&s, ":"))
2214 goto error;
2215 READ_NUM(y, y_per);
2216 }
2217
2218 return s.len == 0;
2219
2220 error:
2221 return false;
2222 }
2223
2224 #undef READ_NUM
2225 #undef READ_SIGN
2226
2227 #define APPEND_PER(F, F_PER) \
2228 res = talloc_asprintf_append(res, "%d%s", gm->F, gm->F_PER ? "%" : "")
2229
print_geometry(const m_option_t * opt,const void * val)2230 static char *print_geometry(const m_option_t *opt, const void *val)
2231 {
2232 const struct m_geometry *gm = val;
2233 char *res = talloc_strdup(NULL, "");
2234 if (gm->wh_valid || gm->xy_valid) {
2235 if (gm->wh_valid) {
2236 APPEND_PER(w, w_per);
2237 res = talloc_asprintf_append(res, "x");
2238 APPEND_PER(h, h_per);
2239 }
2240 if (gm->xy_valid) {
2241 res = talloc_asprintf_append(res, gm->x_sign ? "-" : "+");
2242 APPEND_PER(x, x_per);
2243 res = talloc_asprintf_append(res, gm->y_sign ? "-" : "+");
2244 APPEND_PER(y, y_per);
2245 }
2246 if (gm->ws > 0)
2247 res = talloc_asprintf_append(res, "/%d", gm->ws);
2248 }
2249 return res;
2250 }
2251
2252 #undef APPEND_PER
2253
2254 // xpos,ypos: position of the left upper corner
2255 // widw,widh: width and height of the window
2256 // scrw,scrh: width and height of the current screen
2257 // The input parameters should be set to a centered window (default fallbacks).
m_geometry_apply(int * xpos,int * ypos,int * widw,int * widh,int scrw,int scrh,struct m_geometry * gm)2258 void m_geometry_apply(int *xpos, int *ypos, int *widw, int *widh,
2259 int scrw, int scrh, struct m_geometry *gm)
2260 {
2261 if (gm->wh_valid) {
2262 int prew = *widw, preh = *widh;
2263 if (gm->w > 0)
2264 *widw = gm->w_per ? scrw * (gm->w / 100.0) : gm->w;
2265 if (gm->h > 0)
2266 *widh = gm->h_per ? scrh * (gm->h / 100.0) : gm->h;
2267 // keep aspect if the other value is not set
2268 double asp = (double)prew / preh;
2269 if (gm->w > 0 && !(gm->h > 0)) {
2270 *widh = *widw / asp;
2271 } else if (!(gm->w > 0) && gm->h > 0) {
2272 *widw = *widh * asp;
2273 }
2274 // Center window after resize. If valid x:y values are passed to
2275 // geometry, then those values will be overriden.
2276 *xpos += prew / 2 - *widw / 2;
2277 *ypos += preh / 2 - *widh / 2;
2278 }
2279
2280 if (gm->xy_valid) {
2281 if (gm->x != INT_MIN) {
2282 *xpos = gm->x;
2283 if (gm->x_per)
2284 *xpos = (scrw - *widw) * (*xpos / 100.0);
2285 if (gm->x_sign)
2286 *xpos = scrw - *widw - *xpos;
2287 }
2288 if (gm->y != INT_MIN) {
2289 *ypos = gm->y;
2290 if (gm->y_per)
2291 *ypos = (scrh - *widh) * (*ypos / 100.0);
2292 if (gm->y_sign)
2293 *ypos = scrh - *widh - *ypos;
2294 }
2295 }
2296 }
2297
parse_geometry(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2298 static int parse_geometry(struct mp_log *log, const m_option_t *opt,
2299 struct bstr name, struct bstr param, void *dst)
2300 {
2301 bool is_help = bstr_equals0(param, "help");
2302 if (is_help)
2303 goto exit;
2304
2305 struct m_geometry gm;
2306 if (!parse_geometry_str(&gm, param))
2307 goto exit;
2308
2309 if (dst)
2310 *((struct m_geometry *)dst) = gm;
2311
2312 return 1;
2313
2314 exit:
2315 if (!is_help) {
2316 mp_err(log, "Option %.*s: invalid geometry: '%.*s'\n",
2317 BSTR_P(name), BSTR_P(param));
2318 }
2319 mp_info(log,
2320 "Valid format: [W[%%][xH[%%]]][{+-}X[%%]{+-}Y[%%]] | [X[%%]:Y[%%]]\n");
2321 return is_help ? M_OPT_EXIT : M_OPT_INVALID;
2322 }
2323
geometry_equal(const m_option_t * opt,void * a,void * b)2324 static bool geometry_equal(const m_option_t *opt, void *a, void *b)
2325 {
2326 struct m_geometry *ga = a;
2327 struct m_geometry *gb = b;
2328 return ga->x == gb->x && ga->y == gb->y && ga->w == gb->w && ga->h == gb->h &&
2329 ga->xy_valid == gb->xy_valid && ga->wh_valid == gb->wh_valid &&
2330 ga->w_per == gb->w_per && ga->h_per == gb->h_per &&
2331 ga->x_per == gb->x_per && ga->y_per == gb->y_per &&
2332 ga->x_sign == gb->x_sign && ga->y_sign == gb->y_sign &&
2333 ga->ws == gb->ws;
2334 }
2335
2336 const m_option_type_t m_option_type_geometry = {
2337 .name = "Window geometry",
2338 .size = sizeof(struct m_geometry),
2339 .parse = parse_geometry,
2340 .print = print_geometry,
2341 .copy = copy_opt,
2342 .equal = geometry_equal,
2343 };
2344
parse_size_box(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2345 static int parse_size_box(struct mp_log *log, const m_option_t *opt,
2346 struct bstr name, struct bstr param, void *dst)
2347 {
2348 bool is_help = bstr_equals0(param, "help");
2349 if (is_help)
2350 goto exit;
2351
2352 struct m_geometry gm;
2353 if (!parse_geometry_str(&gm, param))
2354 goto exit;
2355
2356 if (gm.xy_valid)
2357 goto exit;
2358
2359 if (dst)
2360 *((struct m_geometry *)dst) = gm;
2361
2362 return 1;
2363
2364 exit:
2365 if (!is_help) {
2366 mp_err(log, "Option %.*s: invalid size: '%.*s'\n",
2367 BSTR_P(name), BSTR_P(param));
2368 }
2369 mp_info(log, "Valid format: W[%%][xH[%%]] or empty string\n");
2370 return is_help ? M_OPT_EXIT : M_OPT_INVALID;
2371 }
2372
2373 const m_option_type_t m_option_type_size_box = {
2374 .name = "Window size",
2375 .size = sizeof(struct m_geometry),
2376 .parse = parse_size_box,
2377 .print = print_geometry,
2378 .copy = copy_opt,
2379 .equal = geometry_equal,
2380 };
2381
2382
2383 #include "video/img_format.h"
2384
parse_imgfmt(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2385 static int parse_imgfmt(struct mp_log *log, const m_option_t *opt,
2386 struct bstr name, struct bstr param, void *dst)
2387 {
2388 if (param.len == 0)
2389 return M_OPT_MISSING_PARAM;
2390
2391 if (!bstrcmp0(param, "help")) {
2392 mp_info(log, "Available formats:");
2393 char **list = mp_imgfmt_name_list();
2394 for (int i = 0; list[i]; i++)
2395 mp_info(log, " %s", list[i]);
2396 mp_info(log, " no");
2397 mp_info(log, "\n");
2398 talloc_free(list);
2399 return M_OPT_EXIT;
2400 }
2401
2402 unsigned int fmt = mp_imgfmt_from_name(param);
2403 if (!fmt && !bstr_equals0(param, "no")) {
2404 mp_err(log, "Option %.*s: unknown format name: '%.*s'\n",
2405 BSTR_P(name), BSTR_P(param));
2406 return M_OPT_INVALID;
2407 }
2408
2409 if (dst)
2410 *((int *)dst) = fmt;
2411
2412 return 1;
2413 }
2414
print_imgfmt(const m_option_t * opt,const void * val)2415 static char *print_imgfmt(const m_option_t *opt, const void *val)
2416 {
2417 int fmt = *(int *)val;
2418 return talloc_strdup(NULL, fmt ? mp_imgfmt_to_name(fmt) : "no");
2419 }
2420
2421 const m_option_type_t m_option_type_imgfmt = {
2422 .name = "Image format",
2423 .size = sizeof(int),
2424 .parse = parse_imgfmt,
2425 .print = print_imgfmt,
2426 .copy = copy_opt,
2427 .equal = int_equal,
2428 };
2429
parse_fourcc(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2430 static int parse_fourcc(struct mp_log *log, const m_option_t *opt,
2431 struct bstr name, struct bstr param, void *dst)
2432 {
2433 if (param.len == 0)
2434 return M_OPT_MISSING_PARAM;
2435
2436 unsigned int value;
2437
2438 if (param.len == 4) {
2439 uint8_t *s = param.start;
2440 value = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
2441 } else {
2442 bstr rest;
2443 value = bstrtoll(param, &rest, 16);
2444 if (rest.len != 0) {
2445 mp_err(log, "Option %.*s: invalid FourCC: '%.*s'\n",
2446 BSTR_P(name), BSTR_P(param));
2447 return M_OPT_INVALID;
2448 }
2449 }
2450
2451 if (dst)
2452 *((unsigned int *)dst) = value;
2453
2454 return 1;
2455 }
2456
print_fourcc(const m_option_t * opt,const void * val)2457 static char *print_fourcc(const m_option_t *opt, const void *val)
2458 {
2459 unsigned int fourcc = *(unsigned int *)val;
2460 return talloc_asprintf(NULL, "%08x", fourcc);
2461 }
2462
2463 const m_option_type_t m_option_type_fourcc = {
2464 .name = "FourCC",
2465 .size = sizeof(unsigned int),
2466 .parse = parse_fourcc,
2467 .print = print_fourcc,
2468 .copy = copy_opt,
2469 .equal = int_equal,
2470 };
2471
2472 #include "audio/format.h"
2473
parse_afmt(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2474 static int parse_afmt(struct mp_log *log, const m_option_t *opt,
2475 struct bstr name, struct bstr param, void *dst)
2476 {
2477 if (param.len == 0)
2478 return M_OPT_MISSING_PARAM;
2479
2480 if (!bstrcmp0(param, "help")) {
2481 mp_info(log, "Available formats:");
2482 for (int i = 1; i < AF_FORMAT_COUNT; i++)
2483 mp_info(log, " %s", af_fmt_to_str(i));
2484 mp_info(log, "\n");
2485 return M_OPT_EXIT;
2486 }
2487
2488 int fmt = 0;
2489 for (int i = 1; i < AF_FORMAT_COUNT; i++) {
2490 if (bstr_equals0(param, af_fmt_to_str(i)))
2491 fmt = i;
2492 }
2493 if (!fmt) {
2494 mp_err(log, "Option %.*s: unknown format name: '%.*s'\n",
2495 BSTR_P(name), BSTR_P(param));
2496 return M_OPT_INVALID;
2497 }
2498
2499 if (dst)
2500 *((int *)dst) = fmt;
2501
2502 return 1;
2503 }
2504
print_afmt(const m_option_t * opt,const void * val)2505 static char *print_afmt(const m_option_t *opt, const void *val)
2506 {
2507 int fmt = *(int *)val;
2508 return talloc_strdup(NULL, fmt ? af_fmt_to_str(fmt) : "no");
2509 }
2510
2511 const m_option_type_t m_option_type_afmt = {
2512 .name = "Audio format",
2513 .size = sizeof(int),
2514 .parse = parse_afmt,
2515 .print = print_afmt,
2516 .copy = copy_opt,
2517 .equal = int_equal,
2518 };
2519
2520 #include "audio/chmap.h"
2521
parse_channels(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2522 static int parse_channels(struct mp_log *log, const m_option_t *opt,
2523 struct bstr name, struct bstr param, void *dst)
2524 {
2525 bool limited = opt->flags & M_OPT_CHANNELS_LIMITED;
2526
2527 struct m_channels res = {0};
2528
2529 if (bstr_equals0(param, "help")) {
2530 mp_chmap_print_help(log);
2531 if (!limited) {
2532 mp_info(log, "\nOther values:\n"
2533 " auto-safe\n");
2534 }
2535 return M_OPT_EXIT;
2536 }
2537
2538 bool auto_safe = bstr_equals0(param, "auto-safe");
2539 if (bstr_equals0(param, "auto") || bstr_equals0(param, "empty") || auto_safe) {
2540 if (limited) {
2541 mp_err(log, "Disallowed parameter.\n");
2542 return M_OPT_INVALID;
2543 }
2544 param.len = 0;
2545 res.set = true;
2546 res.auto_safe = auto_safe;
2547 }
2548
2549 while (param.len) {
2550 bstr item;
2551 if (limited) {
2552 item = param;
2553 param.len = 0;
2554 } else {
2555 bstr_split_tok(param, ",", &item, ¶m);
2556 }
2557
2558 struct mp_chmap map = {0};
2559 if (!mp_chmap_from_str(&map, item) || !mp_chmap_is_valid(&map)) {
2560 mp_err(log, "Invalid channel layout: %.*s\n", BSTR_P(item));
2561 talloc_free(res.chmaps);
2562 return M_OPT_INVALID;
2563 }
2564
2565 MP_TARRAY_APPEND(NULL, res.chmaps, res.num_chmaps, map);
2566 res.set = true;
2567 }
2568
2569 if (dst) {
2570 *(struct m_channels *)dst = res;
2571 } else {
2572 talloc_free(res.chmaps);
2573 }
2574
2575 return 1;
2576 }
2577
print_channels(const m_option_t * opt,const void * val)2578 static char *print_channels(const m_option_t *opt, const void *val)
2579 {
2580 const struct m_channels *ch = val;
2581 if (!ch->set)
2582 return talloc_strdup(NULL, "");
2583 if (ch->auto_safe)
2584 return talloc_strdup(NULL, "auto-safe");
2585 if (ch->num_chmaps > 0) {
2586 char *res = talloc_strdup(NULL, "");
2587 for (int n = 0; n < ch->num_chmaps; n++) {
2588 if (n > 0)
2589 res = talloc_strdup_append(res, ",");
2590 res = talloc_strdup_append(res, mp_chmap_to_str(&ch->chmaps[n]));
2591 }
2592 return res;
2593 }
2594 return talloc_strdup(NULL, "auto");
2595 }
2596
free_channels(void * src)2597 static void free_channels(void *src)
2598 {
2599 if (!src)
2600 return;
2601
2602 struct m_channels *ch = src;
2603 talloc_free(ch->chmaps);
2604 *ch = (struct m_channels){0};
2605 }
2606
copy_channels(const m_option_t * opt,void * dst,const void * src)2607 static void copy_channels(const m_option_t *opt, void *dst, const void *src)
2608 {
2609 if (!(dst && src))
2610 return;
2611
2612 struct m_channels *ch = dst;
2613 free_channels(dst);
2614 *ch = *(struct m_channels *)src;
2615 ch->chmaps =
2616 talloc_memdup(NULL, ch->chmaps, sizeof(ch->chmaps[0]) * ch->num_chmaps);
2617 }
2618
channels_equal(const m_option_t * opt,void * a,void * b)2619 static bool channels_equal(const m_option_t *opt, void *a, void *b)
2620 {
2621 struct m_channels *ca = a;
2622 struct m_channels *cb = b;
2623
2624 if (ca->set != cb->set ||
2625 ca->auto_safe != cb->auto_safe ||
2626 ca->num_chmaps != cb->num_chmaps)
2627 return false;
2628
2629 for (int n = 0; n < ca->num_chmaps; n++) {
2630 if (!mp_chmap_equals(&ca->chmaps[n], &cb->chmaps[n]))
2631 return false;
2632 }
2633
2634 return true;
2635 }
2636
2637 const m_option_type_t m_option_type_channels = {
2638 .name = "Audio channels or channel map",
2639 .size = sizeof(struct m_channels),
2640 .parse = parse_channels,
2641 .print = print_channels,
2642 .copy = copy_channels,
2643 .free = free_channels,
2644 .equal = channels_equal,
2645 };
2646
parse_timestring(struct bstr str,double * time,char endchar)2647 static int parse_timestring(struct bstr str, double *time, char endchar)
2648 {
2649 int a, b, len;
2650 double d;
2651 *time = 0; /* ensure initialization for error cases */
2652 if (bstr_sscanf(str, "%d:%d:%lf%n", &a, &b, &d, &len) >= 3)
2653 *time = 3600 * a + 60 * b + d;
2654 else if (bstr_sscanf(str, "%d:%lf%n", &a, &d, &len) >= 2)
2655 *time = 60 * a + d;
2656 else if (bstr_sscanf(str, "%lf%n", &d, &len) >= 1)
2657 *time = d;
2658 else
2659 return 0; /* unsupported time format */
2660 if (len < str.len && str.start[len] != endchar)
2661 return 0; /* invalid extra characters at the end */
2662 if (!isfinite(*time))
2663 return 0;
2664 return len;
2665 }
2666
2667 #define HAS_NOPTS(opt) ((opt)->flags & M_OPT_ALLOW_NO)
2668
parse_time(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2669 static int parse_time(struct mp_log *log, const m_option_t *opt,
2670 struct bstr name, struct bstr param, void *dst)
2671 {
2672 if (param.len == 0)
2673 return M_OPT_MISSING_PARAM;
2674
2675 double time = MP_NOPTS_VALUE;
2676 if (HAS_NOPTS(opt) && bstr_equals0(param, "no")) {
2677 // nothing
2678 } else if (!parse_timestring(param, &time, 0)) {
2679 mp_err(log, "Option %.*s: invalid time: '%.*s'\n",
2680 BSTR_P(name), BSTR_P(param));
2681 return M_OPT_INVALID;
2682 }
2683
2684 if (dst)
2685 *(double *)dst = time;
2686 return 1;
2687 }
2688
print_time(const m_option_t * opt,const void * val)2689 static char *print_time(const m_option_t *opt, const void *val)
2690 {
2691 double pts = *(double *)val;
2692 if (pts == MP_NOPTS_VALUE && HAS_NOPTS(opt))
2693 return talloc_strdup(NULL, "no"); // symmetry with parsing
2694 return talloc_asprintf(NULL, "%f", pts);
2695 }
2696
pretty_print_time(const m_option_t * opt,const void * val)2697 static char *pretty_print_time(const m_option_t *opt, const void *val)
2698 {
2699 double pts = *(double *)val;
2700 if (pts == MP_NOPTS_VALUE && HAS_NOPTS(opt))
2701 return talloc_strdup(NULL, "no"); // symmetry with parsing
2702 return mp_format_time(pts, false);
2703 }
2704
time_set(const m_option_t * opt,void * dst,struct mpv_node * src)2705 static int time_set(const m_option_t *opt, void *dst, struct mpv_node *src)
2706 {
2707 if (HAS_NOPTS(opt) && src->format == MPV_FORMAT_STRING) {
2708 if (strcmp(src->u.string, "no") == 0) {
2709 *(double *)dst = MP_NOPTS_VALUE;
2710 return 1;
2711 }
2712 }
2713 return double_set(opt, dst, src);
2714 }
2715
time_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)2716 static int time_get(const m_option_t *opt, void *ta_parent,
2717 struct mpv_node *dst, void *src)
2718 {
2719 if (HAS_NOPTS(opt) && *(double *)src == MP_NOPTS_VALUE) {
2720 dst->format = MPV_FORMAT_STRING;
2721 dst->u.string = talloc_strdup(ta_parent, "no");
2722 return 1;
2723 }
2724 return double_get(opt, ta_parent, dst, src);
2725 }
2726
2727 const m_option_type_t m_option_type_time = {
2728 .name = "Time",
2729 .size = sizeof(double),
2730 .parse = parse_time,
2731 .print = print_time,
2732 .pretty_print = pretty_print_time,
2733 .copy = copy_opt,
2734 .add = add_double,
2735 .set = time_set,
2736 .get = time_get,
2737 .equal = double_equal,
2738 };
2739
2740
2741 // Relative time
2742
parse_rel_time(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)2743 static int parse_rel_time(struct mp_log *log, const m_option_t *opt,
2744 struct bstr name, struct bstr param, void *dst)
2745 {
2746 struct m_rel_time t = {0};
2747
2748 if (param.len == 0)
2749 return M_OPT_MISSING_PARAM;
2750
2751 if (bstr_equals0(param, "none")) {
2752 t.type = REL_TIME_NONE;
2753 goto out;
2754 }
2755
2756 // Percent pos
2757 if (bstr_endswith0(param, "%")) {
2758 double percent = bstrtod(bstr_splice(param, 0, -1), ¶m);
2759 if (param.len == 0 && percent >= 0 && percent <= 100) {
2760 t.type = REL_TIME_PERCENT;
2761 t.pos = percent;
2762 goto out;
2763 }
2764 }
2765
2766 // Chapter pos
2767 if (bstr_startswith0(param, "#")) {
2768 int chapter = bstrtoll(bstr_cut(param, 1), ¶m, 10);
2769 if (param.len == 0 && chapter >= 1) {
2770 t.type = REL_TIME_CHAPTER;
2771 t.pos = chapter - 1;
2772 goto out;
2773 }
2774 }
2775
2776 double time;
2777 if (parse_timestring(param, &time, 0)) {
2778 if (bstr_startswith0(param, "+") || bstr_startswith0(param, "-")) {
2779 t.type = REL_TIME_RELATIVE;
2780 } else {
2781 t.type = REL_TIME_ABSOLUTE;
2782 }
2783 t.pos = time;
2784 goto out;
2785 }
2786
2787 mp_err(log, "Option %.*s: invalid time or position: '%.*s'\n",
2788 BSTR_P(name), BSTR_P(param));
2789 return M_OPT_INVALID;
2790
2791 out:
2792 if (dst)
2793 *(struct m_rel_time *)dst = t;
2794 return 1;
2795 }
2796
print_rel_time(const m_option_t * opt,const void * val)2797 static char *print_rel_time(const m_option_t *opt, const void *val)
2798 {
2799 const struct m_rel_time *t = val;
2800 switch(t->type) {
2801 case REL_TIME_ABSOLUTE:
2802 return talloc_asprintf(NULL, "%g", t->pos);
2803 case REL_TIME_RELATIVE:
2804 return talloc_asprintf(NULL, "%s%g",
2805 (t->pos >= 0) ? "+" : "-", fabs(t->pos));
2806 case REL_TIME_CHAPTER:
2807 return talloc_asprintf(NULL, "#%g", t->pos);
2808 case REL_TIME_PERCENT:
2809 return talloc_asprintf(NULL, "%g%%", t->pos);
2810 }
2811 return talloc_strdup(NULL, "none");
2812 }
2813
rel_time_equal(const m_option_t * opt,void * a,void * b)2814 static bool rel_time_equal(const m_option_t *opt, void *a, void *b)
2815 {
2816 struct m_rel_time *ta = a;
2817 struct m_rel_time *tb = b;
2818 return ta->type == tb->type && ta->pos == tb->pos;
2819 }
2820
2821 const m_option_type_t m_option_type_rel_time = {
2822 .name = "Relative time or percent position",
2823 .size = sizeof(struct m_rel_time),
2824 .parse = parse_rel_time,
2825 .print = print_rel_time,
2826 .copy = copy_opt,
2827 .equal = rel_time_equal,
2828 };
2829
2830
2831 //// Objects (i.e. filters, etc) settings
2832
2833 #undef VAL
2834 #define VAL(x) (*(m_obj_settings_t **)(x))
2835
m_obj_list_find(struct m_obj_desc * dst,const struct m_obj_list * l,bstr name)2836 bool m_obj_list_find(struct m_obj_desc *dst, const struct m_obj_list *l,
2837 bstr name)
2838 {
2839 for (int i = 0; ; i++) {
2840 if (!l->get_desc(dst, i))
2841 break;
2842 if (bstr_equals0(name, dst->name))
2843 return true;
2844 }
2845 for (int i = 0; l->aliases[i][0]; i++) {
2846 const char *aname = l->aliases[i][0];
2847 const char *alias = l->aliases[i][1];
2848 if (bstr_equals0(name, aname) && m_obj_list_find(dst, l, bstr0(alias)))
2849 {
2850 dst->replaced_name = aname;
2851 return true;
2852 }
2853 }
2854 return false;
2855 }
2856
obj_setting_free(m_obj_settings_t * item)2857 static void obj_setting_free(m_obj_settings_t *item)
2858 {
2859 talloc_free(item->name);
2860 talloc_free(item->label);
2861 free_str_list(&(item->attribs));
2862 }
2863
2864 // If at least one item has a label, compare labels only - otherwise ignore them.
obj_setting_match(m_obj_settings_t * a,m_obj_settings_t * b)2865 static bool obj_setting_match(m_obj_settings_t *a, m_obj_settings_t *b)
2866 {
2867 bstr la = bstr0(a->label), lb = bstr0(b->label);
2868 if (la.len || lb.len)
2869 return bstr_equals(la, lb);
2870
2871 return m_obj_settings_equal(a, b);
2872 }
2873
obj_settings_list_num_items(m_obj_settings_t * obj_list)2874 static int obj_settings_list_num_items(m_obj_settings_t *obj_list)
2875 {
2876 int num = 0;
2877 while (obj_list && obj_list[num].name)
2878 num++;
2879 return num;
2880 }
2881
obj_settings_list_del_at(m_obj_settings_t ** p_obj_list,int idx)2882 static void obj_settings_list_del_at(m_obj_settings_t **p_obj_list, int idx)
2883 {
2884 m_obj_settings_t *obj_list = *p_obj_list;
2885 int num = obj_settings_list_num_items(obj_list);
2886
2887 assert(idx >= 0 && idx < num);
2888
2889 obj_setting_free(&obj_list[idx]);
2890
2891 // Note: the NULL-terminating element is moved down as part of this
2892 memmove(&obj_list[idx], &obj_list[idx + 1],
2893 sizeof(m_obj_settings_t) * (num - idx));
2894
2895 *p_obj_list = talloc_realloc(NULL, obj_list, struct m_obj_settings, num);
2896 }
2897
2898 // Insert such that *p_obj_list[idx] is set to item.
2899 // If idx < 0, set idx = count + idx + 1 (i.e. -1 inserts it as last element).
2900 // Memory referenced by *item is not copied.
obj_settings_list_insert_at(m_obj_settings_t ** p_obj_list,int idx,m_obj_settings_t * item)2901 static void obj_settings_list_insert_at(m_obj_settings_t **p_obj_list, int idx,
2902 m_obj_settings_t *item)
2903 {
2904 int num = obj_settings_list_num_items(*p_obj_list);
2905 if (idx < 0)
2906 idx = num + idx + 1;
2907 assert(idx >= 0 && idx <= num);
2908 *p_obj_list = talloc_realloc(NULL, *p_obj_list, struct m_obj_settings,
2909 num + 2);
2910 memmove(*p_obj_list + idx + 1, *p_obj_list + idx,
2911 (num - idx) * sizeof(m_obj_settings_t));
2912 (*p_obj_list)[idx] = *item;
2913 (*p_obj_list)[num + 1] = (m_obj_settings_t){0};
2914 }
2915
obj_settings_list_find_by_label(m_obj_settings_t * obj_list,bstr label)2916 static int obj_settings_list_find_by_label(m_obj_settings_t *obj_list,
2917 bstr label)
2918 {
2919 for (int n = 0; obj_list && obj_list[n].name; n++) {
2920 if (label.len && bstr_equals0(label, obj_list[n].label))
2921 return n;
2922 }
2923 return -1;
2924 }
2925
obj_settings_list_find_by_label0(m_obj_settings_t * obj_list,const char * label)2926 static int obj_settings_list_find_by_label0(m_obj_settings_t *obj_list,
2927 const char *label)
2928 {
2929 return obj_settings_list_find_by_label(obj_list, bstr0(label));
2930 }
2931
obj_settings_find_by_content(m_obj_settings_t * obj_list,m_obj_settings_t * item)2932 static int obj_settings_find_by_content(m_obj_settings_t *obj_list,
2933 m_obj_settings_t *item)
2934 {
2935 for (int n = 0; obj_list && obj_list[n].name; n++) {
2936 if (obj_setting_match(&obj_list[n], item))
2937 return n;
2938 }
2939 return -1;
2940 }
2941
free_obj_settings_list(void * dst)2942 static void free_obj_settings_list(void *dst)
2943 {
2944 int n;
2945 m_obj_settings_t *d;
2946
2947 if (!dst || !VAL(dst))
2948 return;
2949
2950 d = VAL(dst);
2951 for (n = 0; d[n].name; n++)
2952 obj_setting_free(&d[n]);
2953 talloc_free(d);
2954 VAL(dst) = NULL;
2955 }
2956
copy_obj_settings_list(const m_option_t * opt,void * dst,const void * src)2957 static void copy_obj_settings_list(const m_option_t *opt, void *dst,
2958 const void *src)
2959 {
2960 m_obj_settings_t *d, *s;
2961 int n;
2962
2963 if (!(dst && src))
2964 return;
2965
2966 s = VAL(src);
2967
2968 if (VAL(dst))
2969 free_obj_settings_list(dst);
2970 if (!s)
2971 return;
2972
2973 for (n = 0; s[n].name; n++)
2974 /* NOP */;
2975 d = talloc_array(NULL, struct m_obj_settings, n + 1);
2976 for (n = 0; s[n].name; n++) {
2977 d[n].name = talloc_strdup(NULL, s[n].name);
2978 d[n].label = talloc_strdup(NULL, s[n].label);
2979 d[n].enabled = s[n].enabled;
2980 d[n].attribs = NULL;
2981 copy_str_list(NULL, &(d[n].attribs), &(s[n].attribs));
2982 }
2983 d[n].name = NULL;
2984 d[n].label = NULL;
2985 d[n].attribs = NULL;
2986 VAL(dst) = d;
2987 }
2988
2989 // Consider -vf a=b=c:d=e. This verifies "b"="c" and "d"="e" and that the
2990 // option names/values are correct. Try to determine whether an option
2991 // without '=' sets a flag, or whether it's a positional argument.
get_obj_param(struct mp_log * log,bstr opt_name,bstr obj_name,struct m_config * config,bstr name,bstr val,int flags,bool nopos,int * nold,bstr * out_name,bstr * out_val,char * tmp,size_t tmp_size)2992 static int get_obj_param(struct mp_log *log, bstr opt_name, bstr obj_name,
2993 struct m_config *config, bstr name, bstr val,
2994 int flags, bool nopos,
2995 int *nold, bstr *out_name, bstr *out_val,
2996 char *tmp, size_t tmp_size)
2997 {
2998 int r;
2999
3000 if (!config) {
3001 // Duplicates the logic below, but with unknown parameter types/names.
3002 if (val.start || nopos) {
3003 *out_name = name;
3004 *out_val = val;
3005 } else {
3006 val = name;
3007 // positional fields
3008 if (val.len == 0) { // Empty field, count it and go on
3009 (*nold)++;
3010 return 0;
3011 }
3012 // Positional naming convention for/followed by mp_set_avopts().
3013 snprintf(tmp, tmp_size, "@%d", *nold);
3014 *out_name = bstr0(tmp);
3015 *out_val = val;
3016 (*nold)++;
3017 }
3018 return 1;
3019 }
3020
3021 // val.start != NULL => of the form name=val (not positional)
3022 // If it's just "name", and the associated option exists and is a flag,
3023 // don't accept it as positional argument.
3024 if (val.start || m_config_option_requires_param(config, name) == 0 || nopos) {
3025 r = m_config_set_option_cli(config, name, val, flags);
3026 if (r < 0) {
3027 if (r == M_OPT_UNKNOWN) {
3028 mp_err(log, "Option %.*s: %.*s doesn't have a %.*s parameter.\n",
3029 BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(name));
3030 return M_OPT_UNKNOWN;
3031 }
3032 if (r != M_OPT_EXIT)
3033 mp_err(log, "Option %.*s: "
3034 "Error while parsing %.*s parameter %.*s (%.*s)\n",
3035 BSTR_P(opt_name), BSTR_P(obj_name), BSTR_P(name),
3036 BSTR_P(val));
3037 return r;
3038 }
3039 *out_name = name;
3040 *out_val = val;
3041 return 1;
3042 } else {
3043 val = name;
3044 // positional fields
3045 if (val.len == 0) { // Empty field, count it and go on
3046 (*nold)++;
3047 return 0;
3048 }
3049 const char *opt = m_config_get_positional_option(config, *nold);
3050 if (!opt) {
3051 mp_err(log, "Option %.*s: %.*s has only %d "
3052 "params, so you can't give more than %d unnamed params.\n",
3053 BSTR_P(opt_name), BSTR_P(obj_name), *nold, *nold);
3054 return M_OPT_OUT_OF_RANGE;
3055 }
3056 r = m_config_set_option_cli(config, bstr0(opt), val, flags);
3057 if (r < 0) {
3058 if (r != M_OPT_EXIT)
3059 mp_err(log, "Option %.*s: "
3060 "Error while parsing %.*s parameter %s (%.*s)\n",
3061 BSTR_P(opt_name), BSTR_P(obj_name), opt, BSTR_P(val));
3062 return r;
3063 }
3064 *out_name = bstr0(opt);
3065 *out_val = val;
3066 (*nold)++;
3067 return 1;
3068 }
3069 }
3070
3071 // Consider -vf a=b:c:d. This parses "b:c:d" into name/value pairs, stored as
3072 // linear array in *_ret. In particular, config contains what options a the
3073 // object takes, and verifies the option values as well.
3074 // If config is NULL, all parameters are accepted without checking.
3075 // _ret set to NULL can be used for checking-only.
3076 // flags can contain any M_SETOPT_* flag.
3077 // desc is optional.
m_obj_parse_sub_config(struct mp_log * log,struct bstr opt_name,struct bstr name,struct bstr * pstr,struct m_config * config,int flags,bool nopos,struct m_obj_desc * desc,const struct m_obj_list * list,char *** ret)3078 static int m_obj_parse_sub_config(struct mp_log *log, struct bstr opt_name,
3079 struct bstr name, struct bstr *pstr,
3080 struct m_config *config, int flags, bool nopos,
3081 struct m_obj_desc *desc,
3082 const struct m_obj_list *list, char ***ret)
3083 {
3084 int nold = 0;
3085 char **args = NULL;
3086 int num_args = 0;
3087 int r = 1;
3088 char tmp[80];
3089
3090 if (ret) {
3091 args = *ret;
3092 while (args && args[num_args])
3093 num_args++;
3094 }
3095
3096 while (pstr->len > 0) {
3097 bstr fname, fval;
3098 r = split_subconf(log, opt_name, pstr, &fname, &fval);
3099 if (r < 0)
3100 goto exit;
3101
3102 if (list->use_global_options) {
3103 mp_err(log, "Option %.*s: this option does not accept sub-options.\n",
3104 BSTR_P(opt_name));
3105 mp_err(log, "Sub-options for --vo and --ao were removed from mpv in "
3106 "release 0.23.0.\nSee https://0x0.st/uM for details.\n");
3107 r = M_OPT_INVALID;
3108 goto exit;
3109 }
3110
3111 if (bstr_equals0(fname, "help"))
3112 goto print_help;
3113 r = get_obj_param(log, opt_name, name, config, fname, fval, flags,
3114 nopos, &nold, &fname, &fval, tmp, sizeof(tmp));
3115 if (r < 0)
3116 goto exit;
3117
3118 if (r > 0 && ret) {
3119 MP_TARRAY_APPEND(NULL, args, num_args, bstrto0(NULL, fname));
3120 MP_TARRAY_APPEND(NULL, args, num_args, bstrto0(NULL, fval));
3121 MP_TARRAY_APPEND(NULL, args, num_args, NULL);
3122 MP_TARRAY_APPEND(NULL, args, num_args, NULL);
3123 num_args -= 2;
3124 }
3125
3126 if (!bstr_eatstart0(pstr, ":"))
3127 break;
3128 }
3129
3130 if (ret) {
3131 if (num_args > 0) {
3132 *ret = args;
3133 args = NULL;
3134 } else {
3135 *ret = NULL;
3136 }
3137 }
3138
3139 goto exit;
3140
3141 print_help: ;
3142 if (config) {
3143 if (desc->print_help)
3144 desc->print_help(log);
3145 m_config_print_option_list(config, "*");
3146 } else if (list->print_unknown_entry_help) {
3147 list->print_unknown_entry_help(log, mp_tprintf(80, "%.*s", BSTR_P(name)));
3148 } else {
3149 mp_warn(log, "Option %.*s: item %.*s doesn't exist.\n",
3150 BSTR_P(opt_name), BSTR_P(name));
3151 }
3152 r = M_OPT_EXIT;
3153
3154 exit:
3155 free_str_list(&args);
3156 return r;
3157 }
3158
3159 // Characters which may appear in a filter name
3160 #define NAMECH "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
3161
3162 // Parse one item, e.g. -vf a=b:c:d,e=f:g => parse a=b:c:d into "a" and "b:c:d"
parse_obj_settings(struct mp_log * log,struct bstr opt,int op,struct bstr * pstr,const struct m_obj_list * list,m_obj_settings_t ** _ret)3163 static int parse_obj_settings(struct mp_log *log, struct bstr opt, int op,
3164 struct bstr *pstr, const struct m_obj_list *list,
3165 m_obj_settings_t **_ret)
3166 {
3167 int r;
3168 char **plist = NULL;
3169 struct m_obj_desc desc;
3170 bstr str = {0};
3171 bstr label = {0};
3172 bool nopos = list->disallow_positional_parameters;
3173 bool enabled = true;
3174
3175 if (bstr_eatstart0(pstr, "@")) {
3176 bstr rest;
3177 if (!bstr_split_tok(*pstr, ":", &label, &rest)) {
3178 // "@labelname" is the special enable/disable toggle syntax
3179 if (op == OP_TOGGLE) {
3180 int idx = bstrspn(*pstr, NAMECH);
3181 label = bstr_splice(*pstr, 0, idx);
3182 if (label.len) {
3183 *pstr = bstr_cut(*pstr, idx);
3184 goto done;
3185 }
3186 }
3187 mp_err(log, "Option %.*s: ':' expected after label.\n", BSTR_P(opt));
3188 return M_OPT_INVALID;
3189 }
3190 *pstr = rest;
3191 if (label.len == 0) {
3192 mp_err(log, "Option %.*s: label name expected.\n", BSTR_P(opt));
3193 return M_OPT_INVALID;
3194 }
3195 }
3196
3197 if (list->allow_disable_entries && bstr_eatstart0(pstr, "!"))
3198 enabled = false;
3199
3200 bool has_param = false;
3201 int idx = bstrspn(*pstr, NAMECH);
3202 str = bstr_splice(*pstr, 0, idx);
3203 if (!str.len) {
3204 mp_err(log, "Option %.*s: filter name expected.\n", BSTR_P(opt));
3205 return M_OPT_INVALID;
3206 }
3207 *pstr = bstr_cut(*pstr, idx);
3208 // video filters use "=", VOs use ":"
3209 if (bstr_eatstart0(pstr, "=") || bstr_eatstart0(pstr, ":"))
3210 has_param = true;
3211
3212 bool skip = false;
3213 if (m_obj_list_find(&desc, list, str)) {
3214 if (desc.replaced_name)
3215 mp_warn(log, "Driver '%s' has been replaced with '%s'!\n",
3216 desc.replaced_name, desc.name);
3217 } else {
3218 char name[80];
3219 snprintf(name, sizeof(name), "%.*s", BSTR_P(str));
3220 if (!list->allow_unknown_entries ||
3221 (list->check_unknown_entry && !list->check_unknown_entry(name)))
3222 {
3223 mp_err(log, "Option %.*s: %.*s doesn't exist.\n",
3224 BSTR_P(opt), BSTR_P(str));
3225 return M_OPT_INVALID;
3226 }
3227 desc = (struct m_obj_desc){0};
3228 skip = true;
3229 }
3230
3231 if (has_param) {
3232 struct m_config *config = NULL;
3233 if (!skip)
3234 config = m_config_from_obj_desc_noalloc(NULL, log, &desc);
3235 r = m_obj_parse_sub_config(log, opt, str, pstr, config,
3236 M_SETOPT_CHECK_ONLY, nopos, &desc, list,
3237 _ret ? &plist : NULL);
3238 talloc_free(config);
3239 if (r < 0)
3240 return r;
3241 }
3242 if (!_ret)
3243 return 1;
3244
3245 done: ;
3246 m_obj_settings_t item = {
3247 .name = bstrto0(NULL, str),
3248 .label = bstrdup0(NULL, label),
3249 .enabled = enabled,
3250 .attribs = plist,
3251 };
3252 obj_settings_list_insert_at(_ret, -1, &item);
3253 return 1;
3254 }
3255
3256 // Parse a single entry for -vf-del (return 0 if not applicable)
3257 // mark_del is bounded by the number of items in dst
parse_obj_settings_del(struct mp_log * log,struct bstr opt_name,struct bstr * param,int op,void * dst,bool * mark_del)3258 static int parse_obj_settings_del(struct mp_log *log, struct bstr opt_name,
3259 struct bstr *param, int op,
3260 void *dst, bool *mark_del)
3261 {
3262 bstr s = *param;
3263 if (bstr_eatstart0(&s, "@")) {
3264 // '@name:' -> parse as normal filter entry
3265 // '@name,' or '@name<end>' -> parse here
3266 int idx = bstrspn(s, NAMECH);
3267 bstr label = bstr_splice(s, 0, idx);
3268 s = bstr_cut(s, idx);
3269 if (bstr_startswith0(s, ":"))
3270 return 0;
3271 if (dst) {
3272 int label_index = obj_settings_list_find_by_label(VAL(dst), label);
3273 if (label_index >= 0) {
3274 mark_del[label_index] = true;
3275 } else {
3276 mp_warn(log, "Option %.*s: item label @%.*s not found.\n",
3277 BSTR_P(opt_name), BSTR_P(label));
3278 }
3279 }
3280 *param = s;
3281 return 1;
3282 }
3283
3284 if (op == OP_REMOVE)
3285 return 0;
3286
3287 bstr rest;
3288 long long id = bstrtoll(s, &rest, 0);
3289 if (rest.len == s.len)
3290 return 0;
3291
3292 if (dst) {
3293 int num = obj_settings_list_num_items(VAL(dst));
3294 if (id < 0)
3295 id = num + id;
3296
3297 if (id >= 0 && id < num) {
3298 mark_del[id] = true;
3299 } else {
3300 mp_warn(log, "Option %.*s: Index %lld is out of range.\n",
3301 BSTR_P(opt_name), id);
3302 }
3303 }
3304
3305 *param = rest;
3306 return 1;
3307 }
3308
parse_obj_settings_list(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)3309 static int parse_obj_settings_list(struct mp_log *log, const m_option_t *opt,
3310 struct bstr name, struct bstr param, void *dst)
3311 {
3312 m_obj_settings_t *res = NULL;
3313 int op = OP_NONE;
3314 bool *mark_del = NULL;
3315 int num_items = obj_settings_list_num_items(dst ? VAL(dst) : 0);
3316 struct m_obj_list *ol = opt->priv;
3317
3318 assert(opt->priv);
3319
3320 if (bstr_endswith0(name, "-add")) {
3321 op = OP_ADD;
3322 } else if (bstr_endswith0(name, "-append")) {
3323 op = OP_APPEND;
3324 } else if (bstr_endswith0(name, "-set")) {
3325 op = OP_NONE;
3326 } else if (bstr_endswith0(name, "-pre")) {
3327 op = OP_PRE;
3328 } else if (bstr_endswith0(name, "-del")) {
3329 op = OP_DEL;
3330 mp_warn(log, "Option %.*s: -del is deprecated! "
3331 "Use -remove (removes by content instead of by index).\n",
3332 BSTR_P(name));
3333 } else if (bstr_endswith0(name, "-remove")) {
3334 op = OP_REMOVE;
3335 } else if (bstr_endswith0(name, "-clr")) {
3336 op = OP_CLR;
3337 } else if (bstr_endswith0(name, "-toggle")) {
3338 op = OP_TOGGLE;
3339 } else if (bstr_endswith0(name, "-help")) {
3340 mp_err(log, "Option %s:\n"
3341 "Supported operations are:\n"
3342 " %s-set\n"
3343 " Overwrite the old list with the given list\n\n"
3344 " %s-append\n"
3345 " Append the given filter to the current list\n\n"
3346 " %s-add\n"
3347 " Append the given list to the current list\n\n"
3348 " %s-pre\n"
3349 " Prepend the given list to the current list\n\n"
3350 " %s-remove\n"
3351 " Remove the given filter from the current list\n\n"
3352 " %s-del x,y,...\n"
3353 " Remove the given elements. Take the list element index (starting from 0).\n"
3354 " Negative index can be used (i.e. -1 is the last element).\n"
3355 " Filter names work as well.\n\n"
3356 " %s-toggle\n"
3357 " Add the filter to the list, or remove it if it's already added.\n\n"
3358 " %s-clr\n"
3359 " Clear the current list.\n\n",
3360 opt->name, opt->name, opt->name, opt->name, opt->name,
3361 opt->name, opt->name, opt->name, opt->name);
3362
3363 return M_OPT_EXIT;
3364 }
3365
3366 if (!bstrcmp0(param, "help")) {
3367 mp_info(log, "Available %s:\n", ol->description);
3368 for (int n = 0; ; n++) {
3369 struct m_obj_desc desc;
3370 if (!ol->get_desc(&desc, n))
3371 break;
3372 if (!desc.hidden) {
3373 mp_info(log, " %-16s %s\n",
3374 desc.name, desc.description);
3375 }
3376 }
3377 mp_info(log, "\n");
3378 if (ol->print_help_list)
3379 ol->print_help_list(log);
3380 if (!ol->use_global_options) {
3381 mp_info(log, "Get help on individual entries via: --%s=entry=help\n",
3382 opt->name);
3383 }
3384 return M_OPT_EXIT;
3385 }
3386
3387 if (op == OP_CLR) {
3388 if (param.len) {
3389 mp_err(log, "Option %.*s: -clr does not take an argument.\n",
3390 BSTR_P(name));
3391 return M_OPT_INVALID;
3392 }
3393 if (dst)
3394 free_obj_settings_list(dst);
3395 return 0;
3396 } else if (op == OP_DEL || op == OP_REMOVE) {
3397 mark_del = talloc_zero_array(NULL, bool, num_items + 1);
3398 }
3399
3400 if (op != OP_NONE && param.len == 0)
3401 return M_OPT_MISSING_PARAM;
3402
3403 while (param.len > 0) {
3404 int r = 0;
3405 if (op == OP_DEL || op == OP_REMOVE)
3406 r = parse_obj_settings_del(log, name, ¶m, op, dst, mark_del);
3407 if (r == 0) {
3408 r = parse_obj_settings(log, name, op, ¶m, ol, dst ? &res : NULL);
3409 }
3410 if (r < 0)
3411 return r;
3412 if (param.len > 0) {
3413 const char sep[2] = {OPTION_LIST_SEPARATOR, 0};
3414 if (!bstr_eatstart0(¶m, sep))
3415 return M_OPT_INVALID;
3416 if (param.len == 0) {
3417 if (!ol->allow_trailer)
3418 return M_OPT_INVALID;
3419 if (dst) {
3420 m_obj_settings_t item = {
3421 .name = talloc_strdup(NULL, ""),
3422 };
3423 obj_settings_list_insert_at(&res, -1, &item);
3424 }
3425 }
3426 }
3427 }
3428
3429 if (op != OP_NONE && res && res[0].name && res[1].name) {
3430 if (op == OP_APPEND) {
3431 mp_err(log, "Option %.*s: -append takes only 1 filter (no ',').\n",
3432 BSTR_P(name));
3433 return M_OPT_INVALID;
3434 }
3435 mp_warn(log, "Passing more than 1 argument to %.*s is deprecated!\n",
3436 BSTR_P(name));
3437 }
3438
3439 if (dst) {
3440 m_obj_settings_t *list = VAL(dst);
3441 if (op == OP_PRE) {
3442 int prepend_counter = 0;
3443 for (int n = 0; res && res[n].name; n++) {
3444 int label = obj_settings_list_find_by_label0(list, res[n].label);
3445 if (label < 0) {
3446 obj_settings_list_insert_at(&list, prepend_counter, &res[n]);
3447 prepend_counter++;
3448 } else {
3449 // Prefer replacement semantics, instead of actually
3450 // prepending.
3451 obj_setting_free(&list[label]);
3452 list[label] = res[n];
3453 }
3454 }
3455 talloc_free(res);
3456 } else if (op == OP_ADD || op == OP_APPEND) {
3457 for (int n = 0; res && res[n].name; n++) {
3458 int label = obj_settings_list_find_by_label0(list, res[n].label);
3459 if (label < 0) {
3460 obj_settings_list_insert_at(&list, -1, &res[n]);
3461 } else {
3462 // Prefer replacement semantics, instead of actually
3463 // appending.
3464 obj_setting_free(&list[label]);
3465 list[label] = res[n];
3466 }
3467 }
3468 talloc_free(res);
3469 } else if (op == OP_TOGGLE) {
3470 for (int n = 0; res && res[n].name; n++) {
3471 if (res[n].label && !res[n].name[0]) {
3472 // Toggle enable/disable special case.
3473 int found =
3474 obj_settings_list_find_by_label0(list, res[n].label);
3475 if (found < 0) {
3476 mp_warn(log, "Option %.*s: Label %s not found\n",
3477 BSTR_P(name), res[n].label);
3478 } else {
3479 list[found].enabled = !list[found].enabled;
3480 }
3481 obj_setting_free(&res[n]);
3482 } else {
3483 int found = obj_settings_find_by_content(list, &res[n]);
3484 if (found < 0) {
3485 obj_settings_list_insert_at(&list, -1, &res[n]);
3486 } else {
3487 obj_settings_list_del_at(&list, found);
3488 obj_setting_free(&res[n]);
3489 }
3490 }
3491 }
3492 talloc_free(res);
3493 } else if (op == OP_DEL || op == OP_REMOVE) {
3494 for (int n = num_items - 1; n >= 0; n--) {
3495 if (mark_del[n])
3496 obj_settings_list_del_at(&list, n);
3497 }
3498 for (int n = 0; res && res[n].name; n++) {
3499 int found = obj_settings_find_by_content(list, &res[n]);
3500 if (found < 0) {
3501 if (op == OP_DEL)
3502 mp_warn(log, "Option %.*s: Item not found\n", BSTR_P(name));
3503 } else {
3504 obj_settings_list_del_at(&list, found);
3505 }
3506 }
3507 free_obj_settings_list(&res);
3508 } else {
3509 assert(op == OP_NONE);
3510 free_obj_settings_list(&list);
3511 list = res;
3512 }
3513 VAL(dst) = list;
3514 }
3515
3516 talloc_free(mark_del);
3517 return 1;
3518 }
3519
append_param(char ** res,char * param)3520 static void append_param(char **res, char *param)
3521 {
3522 if (strspn(param, NAMECH) == strlen(param)) {
3523 *res = talloc_strdup_append(*res, param);
3524 } else {
3525 // Simple escaping: %BYTECOUNT%STRING
3526 *res = talloc_asprintf_append(*res, "%%%zd%%%s", strlen(param), param);
3527 }
3528 }
3529
print_obj_settings_list(const m_option_t * opt,const void * val)3530 static char *print_obj_settings_list(const m_option_t *opt, const void *val)
3531 {
3532 m_obj_settings_t *list = VAL(val);
3533 char *res = talloc_strdup(NULL, "");
3534 for (int n = 0; list && list[n].name; n++) {
3535 m_obj_settings_t *entry = &list[n];
3536 if (n > 0)
3537 res = talloc_strdup_append(res, ",");
3538 // Assume labels and names don't need escaping
3539 if (entry->label && entry->label[0])
3540 res = talloc_asprintf_append(res, "@%s:", entry->label);
3541 if (!entry->enabled)
3542 res = talloc_strdup_append(res, "!");
3543 res = talloc_strdup_append(res, entry->name);
3544 if (entry->attribs && entry->attribs[0]) {
3545 res = talloc_strdup_append(res, "=");
3546 for (int i = 0; entry->attribs[i * 2 + 0]; i++) {
3547 if (i > 0)
3548 res = talloc_strdup_append(res, ":");
3549 append_param(&res, entry->attribs[i * 2 + 0]);
3550 res = talloc_strdup_append(res, "=");
3551 append_param(&res, entry->attribs[i * 2 + 1]);
3552 }
3553 }
3554 }
3555 return res;
3556 }
3557
set_obj_settings_list(const m_option_t * opt,void * dst,struct mpv_node * src)3558 static int set_obj_settings_list(const m_option_t *opt, void *dst,
3559 struct mpv_node *src)
3560 {
3561 if (src->format != MPV_FORMAT_NODE_ARRAY)
3562 return M_OPT_INVALID;
3563 m_obj_settings_t *entries =
3564 talloc_zero_array(NULL, m_obj_settings_t, src->u.list->num + 1);
3565 for (int n = 0; n < src->u.list->num; n++) {
3566 m_obj_settings_t *entry = &entries[n];
3567 entry->enabled = true;
3568 if (src->u.list->values[n].format != MPV_FORMAT_NODE_MAP)
3569 goto error;
3570 struct mpv_node_list *src_entry = src->u.list->values[n].u.list;
3571 for (int i = 0; i < src_entry->num; i++) {
3572 const char *key = src_entry->keys[i];
3573 struct mpv_node *val = &src_entry->values[i];
3574 if (strcmp(key, "name") == 0) {
3575 if (val->format != MPV_FORMAT_STRING)
3576 goto error;
3577 entry->name = talloc_strdup(NULL, val->u.string);
3578 } else if (strcmp(key, "label") == 0) {
3579 if (val->format != MPV_FORMAT_STRING)
3580 goto error;
3581 entry->label = talloc_strdup(NULL, val->u.string);
3582 } else if (strcmp(key, "enabled") == 0) {
3583 if (val->format != MPV_FORMAT_FLAG)
3584 goto error;
3585 entry->enabled = val->u.flag;
3586 } else if (strcmp(key, "params") == 0) {
3587 if (val->format != MPV_FORMAT_NODE_MAP)
3588 goto error;
3589 struct mpv_node_list *src_params = val->u.list;
3590 entry->attribs =
3591 talloc_zero_array(NULL, char*, (src_params->num + 1) * 2);
3592 for (int x = 0; x < src_params->num; x++) {
3593 if (src_params->values[x].format != MPV_FORMAT_STRING)
3594 goto error;
3595 entry->attribs[x * 2 + 0] =
3596 talloc_strdup(NULL, src_params->keys[x]);
3597 entry->attribs[x * 2 + 1] =
3598 talloc_strdup(NULL, src_params->values[x].u.string);
3599 }
3600 }
3601 }
3602 }
3603 free_obj_settings_list(dst);
3604 VAL(dst) = entries;
3605 return 0;
3606 error:
3607 free_obj_settings_list(&entries);
3608 return M_OPT_INVALID;
3609 }
3610
add_array_entry(struct mpv_node * dst)3611 static struct mpv_node *add_array_entry(struct mpv_node *dst)
3612 {
3613 struct mpv_node_list *list = dst->u.list;
3614 assert(dst->format == MPV_FORMAT_NODE_ARRAY&& dst->u.list);
3615 MP_TARRAY_GROW(list, list->values, list->num);
3616 return &list->values[list->num++];
3617 }
3618
add_map_entry(struct mpv_node * dst,const char * key)3619 static struct mpv_node *add_map_entry(struct mpv_node *dst, const char *key)
3620 {
3621 struct mpv_node_list *list = dst->u.list;
3622 assert(dst->format == MPV_FORMAT_NODE_MAP && dst->u.list);
3623 MP_TARRAY_GROW(list, list->values, list->num);
3624 MP_TARRAY_GROW(list, list->keys, list->num);
3625 list->keys[list->num] = talloc_strdup(list, key);
3626 return &list->values[list->num++];
3627 }
3628
add_map_string(struct mpv_node * dst,const char * key,const char * val)3629 static void add_map_string(struct mpv_node *dst, const char *key, const char *val)
3630 {
3631 struct mpv_node *entry = add_map_entry(dst, key);
3632 entry->format = MPV_FORMAT_STRING;
3633 entry->u.string = talloc_strdup(dst->u.list, val);
3634 }
3635
get_obj_settings_list(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * val)3636 static int get_obj_settings_list(const m_option_t *opt, void *ta_parent,
3637 struct mpv_node *dst, void *val)
3638 {
3639 m_obj_settings_t *list = VAL(val);
3640 dst->format = MPV_FORMAT_NODE_ARRAY;
3641 dst->u.list = talloc_zero(ta_parent, struct mpv_node_list);
3642 ta_parent = dst->u.list;
3643 for (int n = 0; list && list[n].name; n++) {
3644 m_obj_settings_t *entry = &list[n];
3645 struct mpv_node *nentry = add_array_entry(dst);
3646 nentry->format = MPV_FORMAT_NODE_MAP;
3647 nentry->u.list = talloc_zero(ta_parent, struct mpv_node_list);
3648 add_map_string(nentry, "name", entry->name);
3649 if (entry->label && entry->label[0])
3650 add_map_string(nentry, "label", entry->label);
3651 struct mpv_node *enabled = add_map_entry(nentry, "enabled");
3652 enabled->format = MPV_FORMAT_FLAG;
3653 enabled->u.flag = entry->enabled;
3654 struct mpv_node *params = add_map_entry(nentry, "params");
3655 params->format = MPV_FORMAT_NODE_MAP;
3656 params->u.list = talloc_zero(ta_parent, struct mpv_node_list);
3657 for (int i = 0; entry->attribs && entry->attribs[i * 2 + 0]; i++) {
3658 add_map_string(params, entry->attribs[i * 2 + 0],
3659 entry->attribs[i * 2 + 1]);
3660 }
3661 }
3662 return 1;
3663 }
3664
obj_settings_list_equal(const m_option_t * opt,void * pa,void * pb)3665 static bool obj_settings_list_equal(const m_option_t *opt, void *pa, void *pb)
3666 {
3667 struct m_obj_settings *a = VAL(pa);
3668 struct m_obj_settings *b = VAL(pb);
3669
3670 if (a == b || !a || !b)
3671 return a == b || (!a && !b[0].name) || (!b && !a[0].name);
3672
3673 for (int n = 0; a[n].name || b[n].name; n++) {
3674 if (!a[n].name || !b[n].name)
3675 return false;
3676 if (!m_obj_settings_equal(&a[n], &b[n]))
3677 return false;
3678 }
3679
3680 return true;
3681 }
3682
m_obj_settings_equal(struct m_obj_settings * a,struct m_obj_settings * b)3683 bool m_obj_settings_equal(struct m_obj_settings *a, struct m_obj_settings *b)
3684 {
3685 if (!str_equal(NULL, &a->name, &b->name))
3686 return false;
3687
3688 if (!str_equal(NULL, &a->label, &b->label))
3689 return false;
3690
3691 if (a->enabled != b->enabled)
3692 return false;
3693
3694 return str_list_equal(NULL, &a->attribs, &b->attribs);
3695 }
3696
3697 const m_option_type_t m_option_type_obj_settings_list = {
3698 .name = "Object settings list",
3699 .size = sizeof(m_obj_settings_t *),
3700 .parse = parse_obj_settings_list,
3701 .print = print_obj_settings_list,
3702 .copy = copy_obj_settings_list,
3703 .free = free_obj_settings_list,
3704 .set = set_obj_settings_list,
3705 .get = get_obj_settings_list,
3706 .equal = obj_settings_list_equal,
3707 .actions = (const struct m_option_action[]){
3708 {"add"},
3709 {"append"},
3710 {"clr", M_OPT_TYPE_OPTIONAL_PARAM},
3711 {"del"},
3712 {"help", M_OPT_TYPE_OPTIONAL_PARAM},
3713 {"pre"},
3714 {"set"},
3715 {"toggle"},
3716 {"remove"},
3717 {0}
3718 },
3719 };
3720
3721 #undef VAL
3722 #define VAL(x) (*(struct mpv_node *)(x))
3723
parse_node(struct mp_log * log,const m_option_t * opt,struct bstr name,struct bstr param,void * dst)3724 static int parse_node(struct mp_log *log, const m_option_t *opt,
3725 struct bstr name, struct bstr param, void *dst)
3726 {
3727 // Maybe use JSON?
3728 mp_err(log, "option type doesn't accept strings");
3729 return M_OPT_INVALID;
3730 }
3731
print_node(const m_option_t * opt,const void * val)3732 static char *print_node(const m_option_t *opt, const void *val)
3733 {
3734 char *t = talloc_strdup(NULL, "");
3735 if (json_write(&t, &VAL(val)) < 0) {
3736 talloc_free(t);
3737 t = NULL;
3738 }
3739 return t;
3740 }
3741
pretty_print_node(const m_option_t * opt,const void * val)3742 static char *pretty_print_node(const m_option_t *opt, const void *val)
3743 {
3744 char *t = talloc_strdup(NULL, "");
3745 if (json_write_pretty(&t, &VAL(val)) < 0) {
3746 talloc_free(t);
3747 t = NULL;
3748 }
3749 return t;
3750 }
3751
dup_node(void * ta_parent,struct mpv_node * node)3752 static void dup_node(void *ta_parent, struct mpv_node *node)
3753 {
3754 switch (node->format) {
3755 case MPV_FORMAT_STRING:
3756 node->u.string = talloc_strdup(ta_parent, node->u.string);
3757 break;
3758 case MPV_FORMAT_NODE_ARRAY:
3759 case MPV_FORMAT_NODE_MAP: {
3760 struct mpv_node_list *oldlist = node->u.list;
3761 struct mpv_node_list *new = talloc_zero(ta_parent, struct mpv_node_list);
3762 node->u.list = new;
3763 if (oldlist->num > 0) {
3764 *new = *oldlist;
3765 new->values = talloc_array(new, struct mpv_node, new->num);
3766 for (int n = 0; n < new->num; n++) {
3767 new->values[n] = oldlist->values[n];
3768 dup_node(new, &new->values[n]);
3769 }
3770 if (node->format == MPV_FORMAT_NODE_MAP) {
3771 new->keys = talloc_array(new, char*, new->num);
3772 for (int n = 0; n < new->num; n++)
3773 new->keys[n] = talloc_strdup(new, oldlist->keys[n]);
3774 }
3775 }
3776 break;
3777 }
3778 case MPV_FORMAT_NONE:
3779 case MPV_FORMAT_FLAG:
3780 case MPV_FORMAT_INT64:
3781 case MPV_FORMAT_DOUBLE:
3782 break;
3783 default:
3784 // unknown entry - mark as invalid
3785 node->format = (mpv_format)-1;
3786 }
3787 }
3788
copy_node(const m_option_t * opt,void * dst,const void * src)3789 static void copy_node(const m_option_t *opt, void *dst, const void *src)
3790 {
3791 assert(sizeof(struct mpv_node) <= sizeof(union m_option_value));
3792
3793 if (!(dst && src))
3794 return;
3795
3796 opt->type->free(dst);
3797 VAL(dst) = VAL(src);
3798 dup_node(NULL, &VAL(dst));
3799 }
3800
node_get_alloc(struct mpv_node * node)3801 void *node_get_alloc(struct mpv_node *node)
3802 {
3803 // Assume it was allocated with copy_node(), which allocates all
3804 // sub-nodes with the parent node as talloc parent.
3805 switch (node->format) {
3806 case MPV_FORMAT_STRING:
3807 return node->u.string;
3808 case MPV_FORMAT_NODE_ARRAY:
3809 case MPV_FORMAT_NODE_MAP:
3810 return node->u.list;
3811 default:
3812 return NULL;
3813 }
3814 }
3815
free_node(void * src)3816 static void free_node(void *src)
3817 {
3818 if (src) {
3819 struct mpv_node *node = &VAL(src);
3820 talloc_free(node_get_alloc(node));
3821 *node = (struct mpv_node){{0}};
3822 }
3823 }
3824
3825 // idempotent functions for convenience
node_set(const m_option_t * opt,void * dst,struct mpv_node * src)3826 static int node_set(const m_option_t *opt, void *dst, struct mpv_node *src)
3827 {
3828 copy_node(opt, dst, src);
3829 return 1;
3830 }
3831
node_get(const m_option_t * opt,void * ta_parent,struct mpv_node * dst,void * src)3832 static int node_get(const m_option_t *opt, void *ta_parent,
3833 struct mpv_node *dst, void *src)
3834 {
3835 *dst = VAL(src);
3836 dup_node(ta_parent, dst);
3837 return 1;
3838 }
3839
node_equal(const m_option_t * opt,void * a,void * b)3840 static bool node_equal(const m_option_t *opt, void *a, void *b)
3841 {
3842 return equal_mpv_node(&VAL(a), &VAL(b));
3843 }
3844
3845 const m_option_type_t m_option_type_node = {
3846 .name = "Complex",
3847 .size = sizeof(struct mpv_node),
3848 .parse = parse_node,
3849 .print = print_node,
3850 .pretty_print = pretty_print_node,
3851 .copy = copy_node,
3852 .free = free_node,
3853 .set = node_set,
3854 .get = node_get,
3855 .equal = node_equal,
3856 };
3857
3858 // Special-cased by m_config.c.
3859 const m_option_type_t m_option_type_alias = {
3860 .name = "alias",
3861 };
3862 const m_option_type_t m_option_type_cli_alias = {
3863 .name = "alias",
3864 };
3865 const m_option_type_t m_option_type_removed = {
3866 .name = "removed",
3867 };
3868 const m_option_type_t m_option_type_subconfig = {
3869 .name = "Subconfig",
3870 };
3871