Coverage Report

Created: 2026-05-16 07:24

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