Coverage Report

Created: 2025-06-24 07:38

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