Coverage Report

Created: 2026-03-27 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/options/m_property.c
Line
Count
Source
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
/// \file
19
/// \ingroup Properties
20
21
#include <stdlib.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <inttypes.h>
25
#include <assert.h>
26
27
#include <libavutil/common.h>
28
29
#include "mpv/client.h"
30
31
#include "mpv_talloc.h"
32
#include "m_option.h"
33
#include "m_property.h"
34
#include "common/msg.h"
35
#include "common/common.h"
36
37
static int m_property_multiply(struct mp_log *log,
38
                               const struct m_property *prop_list,
39
                               const char *property, double f, void *ctx)
40
0
{
41
0
    union m_option_value val = m_option_value_default;
42
0
    struct m_option opt = {0};
43
0
    int r;
44
45
0
    r = m_property_do(log, prop_list, property, M_PROPERTY_GET_CONSTRICTED_TYPE,
46
0
                      &opt, ctx);
47
0
    if (r != M_PROPERTY_OK)
48
0
        return r;
49
0
    mp_assert(opt.type);
50
51
0
    if (!opt.type->multiply)
52
0
        return M_PROPERTY_NOT_IMPLEMENTED;
53
54
0
    r = m_property_do(log, prop_list, property, M_PROPERTY_GET, &val, ctx);
55
0
    if (r != M_PROPERTY_OK)
56
0
        return r;
57
0
    opt.type->multiply(&opt, &val, f);
58
0
    r = m_property_do(log, prop_list, property, M_PROPERTY_SET, &val, ctx);
59
0
    m_option_free(&opt, &val);
60
0
    return r;
61
0
}
62
63
struct m_property *m_property_list_find(const struct m_property *list,
64
                                        const char *name)
65
3.02M
{
66
67.6M
    for (int n = 0; list && list[n].name; n++) {
67
67.6M
        if (strcmp(list[n].name, name) == 0)
68
3.01M
            return (struct m_property *)&list[n];
69
67.6M
    }
70
11.8k
    return NULL;
71
3.02M
}
72
73
static int do_action(const struct m_property *prop_list, const char *name,
74
                     int action, void *arg, void *ctx)
75
3.02M
{
76
3.02M
    struct m_property *prop;
77
3.02M
    struct m_property_action_arg ka;
78
3.02M
    const char *sep = strchr(name, '/');
79
3.02M
    if (sep && sep[1]) {
80
26.5k
        char base[128];
81
26.5k
        snprintf(base, sizeof(base), "%.*s", (int)(sep - name), name);
82
26.5k
        prop = m_property_list_find(prop_list, base);
83
26.5k
        ka = (struct m_property_action_arg) {
84
26.5k
            .key = sep + 1,
85
26.5k
            .action = action,
86
26.5k
            .arg = arg,
87
26.5k
        };
88
26.5k
        action = M_PROPERTY_KEY_ACTION;
89
26.5k
        arg = &ka;
90
26.5k
    } else
91
2.99M
        prop = m_property_list_find(prop_list, name);
92
3.02M
    if (!prop)
93
11.8k
        return M_PROPERTY_UNKNOWN;
94
3.01M
    return prop->call(ctx, prop, action, arg);
95
3.02M
}
96
97
// (as a hack, log can be NULL on read-only paths)
98
int m_property_do(struct mp_log *log, const struct m_property *prop_list,
99
                  const char *name, int action, void *arg, void *ctx)
100
1.30M
{
101
1.30M
    union m_option_value val = m_option_value_default;
102
1.30M
    int r;
103
104
1.30M
    struct m_option opt = {0};
105
1.30M
    r = do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx);
106
1.30M
    if (r <= 0)
107
26.0k
        return r;
108
1.27M
    mp_assert(opt.type);
109
110
1.27M
    switch (action) {
111
3.89k
    case M_PROPERTY_FIXED_LEN_PRINT:
112
435k
    case M_PROPERTY_PRINT: {
113
435k
        if ((r = do_action(prop_list, name, action, arg, ctx)) >= 0)
114
8.35k
            return r;
115
        // Fallback to m_option
116
427k
        if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
117
1.00k
            return r;
118
426k
        char *str = m_option_pretty_print(&opt, &val, action == M_PROPERTY_FIXED_LEN_PRINT);
119
426k
        m_option_free(&opt, &val);
120
426k
        *(char **)arg = str;
121
426k
        return str != NULL;
122
427k
    }
123
825k
    case M_PROPERTY_GET_STRING: {
124
825k
        if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
125
365
            return r;
126
824k
        char *str = m_option_print(&opt, &val);
127
824k
        m_option_free(&opt, &val);
128
824k
        *(char **)arg = str;
129
824k
        return str != NULL;
130
825k
    }
131
0
    case M_PROPERTY_SET_STRING: {
132
0
        struct mpv_node node = { .format = MPV_FORMAT_STRING, .u.string = arg };
133
0
        return m_property_do(log, prop_list, name, M_PROPERTY_SET_NODE, &node, ctx);
134
825k
    }
135
0
    case M_PROPERTY_MULTIPLY: {
136
0
        return m_property_multiply(log, prop_list, name, *(double *)arg, ctx);
137
825k
    }
138
0
    case M_PROPERTY_SWITCH: {
139
0
        if (!log)
140
0
            return M_PROPERTY_ERROR;
141
0
        struct m_property_switch_arg *sarg = arg;
142
0
        if ((r = do_action(prop_list, name, M_PROPERTY_SWITCH, arg, ctx)) !=
143
0
            M_PROPERTY_NOT_IMPLEMENTED)
144
0
            return r;
145
        // Fallback to m_option
146
0
        r = m_property_do(log, prop_list, name, M_PROPERTY_GET_CONSTRICTED_TYPE,
147
0
                          &opt, ctx);
148
0
        if (r <= 0)
149
0
            return r;
150
0
        mp_assert(opt.type);
151
0
        if (!opt.type->add)
152
0
            return M_PROPERTY_NOT_IMPLEMENTED;
153
0
        if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
154
0
            return r;
155
0
        opt.type->add(&opt, &val, sarg->inc, sarg->wrap);
156
0
        r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx);
157
0
        m_option_free(&opt, &val);
158
0
        return r;
159
0
    }
160
0
    case M_PROPERTY_GET_CONSTRICTED_TYPE: {
161
0
        r = do_action(prop_list, name, action, arg, ctx);
162
0
        if (r >= 0 || r == M_PROPERTY_UNAVAILABLE)
163
0
            return r;
164
0
        if ((r = do_action(prop_list, name, M_PROPERTY_GET_TYPE, arg, ctx)) >= 0)
165
0
            return r;
166
0
        return M_PROPERTY_NOT_IMPLEMENTED;
167
0
    }
168
603
    case M_PROPERTY_SET: {
169
603
        return do_action(prop_list, name, M_PROPERTY_SET, arg, ctx);
170
0
    }
171
0
    case M_PROPERTY_GET_NODE: {
172
0
        if ((r = do_action(prop_list, name, M_PROPERTY_GET_NODE, arg, ctx)) !=
173
0
            M_PROPERTY_NOT_IMPLEMENTED)
174
0
            return r;
175
0
        if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
176
0
            return r;
177
0
        struct mpv_node *node = arg;
178
0
        int err = m_option_get_node(&opt, NULL, node, &val);
179
0
        if (err == M_OPT_UNKNOWN) {
180
0
            r = M_PROPERTY_NOT_IMPLEMENTED;
181
0
        } else if (err < 0) {
182
0
            r = M_PROPERTY_INVALID_FORMAT;
183
0
        } else {
184
0
            r = M_PROPERTY_OK;
185
0
        }
186
0
        m_option_free(&opt, &val);
187
0
        return r;
188
0
    }
189
13.5k
    case M_PROPERTY_SET_NODE: {
190
13.5k
        if (!log)
191
0
            return M_PROPERTY_ERROR;
192
13.5k
        if ((r = do_action(prop_list, name, M_PROPERTY_SET_NODE, arg, ctx)) !=
193
13.5k
            M_PROPERTY_NOT_IMPLEMENTED)
194
1.23k
            return r;
195
12.3k
        int err = m_option_set_node_or_string(log, &opt, bstr0(name), &val, arg);
196
12.3k
        if (err == M_OPT_UNKNOWN) {
197
251
            r = M_PROPERTY_NOT_IMPLEMENTED;
198
12.1k
        } else if (err < 0) {
199
1.62k
            r = M_PROPERTY_INVALID_FORMAT;
200
10.4k
        } else {
201
10.4k
            r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx);
202
10.4k
        }
203
12.3k
        m_option_free(&opt, &val);
204
12.3k
        return r;
205
13.5k
    }
206
4.73k
    default:
207
4.73k
        return do_action(prop_list, name, action, arg, ctx);
208
1.27M
    }
209
1.27M
}
210
211
bool m_property_split_path(const char *path, bstr *prefix, const char **rem)
212
26.6k
{
213
26.6k
    const char *next = strchr(path, '/');
214
26.6k
    if (next) {
215
23.0k
        *prefix = bstr_splice(bstr0(path), 0, next - path);
216
23.0k
        *rem = next + 1;
217
23.0k
        return true;
218
23.0k
    } else {
219
3.59k
        *prefix = bstr0(path);
220
3.59k
        *rem = "";
221
3.59k
        return false;
222
3.59k
    }
223
26.6k
}
224
225
// If *action is M_PROPERTY_KEY_ACTION, but the associated path is "", then
226
// make this into a top-level action.
227
static void m_property_unkey(int *action, void **arg)
228
4.51M
{
229
4.51M
    if (*action == M_PROPERTY_KEY_ACTION) {
230
20.2k
        struct m_property_action_arg *ka = *arg;
231
20.2k
        if (!ka->key[0]) {
232
1.95k
            *action = ka->action;
233
1.95k
            *arg = ka->arg;
234
1.95k
        }
235
20.2k
    }
236
4.51M
}
237
238
static int m_property_do_bstr(const struct m_property *prop_list, bstr name,
239
                              int action, void *arg, void *ctx)
240
1.27M
{
241
1.27M
    char *name0 = bstrdup0(NULL, name);
242
1.27M
    int ret = m_property_do(NULL, prop_list, name0, action, arg, ctx);
243
1.27M
    talloc_free(name0);
244
1.27M
    return ret;
245
1.27M
}
246
247
static void append_str(char **s, int *len, bstr append)
248
453k
{
249
453k
    MP_TARRAY_GROW(NULL, *s, *len + append.len);
250
453k
    if (append.len)
251
444k
        memcpy(*s + *len, append.start, append.len);
252
453k
    *len = *len + append.len;
253
453k
}
254
255
static int expand_property(const struct m_property *prop_list, char **ret,
256
                           int *ret_len, bstr prop, bool silent_error, void *ctx)
257
1.27M
{
258
1.27M
    bool cond_yes = bstr_eatstart0(&prop, "?");
259
1.27M
    bool cond_no = !cond_yes && bstr_eatstart0(&prop, "!");
260
1.27M
    bool test = cond_yes || cond_no;
261
1.27M
    bool raw = bstr_eatstart0(&prop, "=");
262
1.27M
    bool fixed_len = !raw && bstr_eatstart0(&prop, ">");
263
1.27M
    bstr comp_with = {0};
264
1.27M
    bool comp = test && bstr_split_tok(prop, "==", &prop, &comp_with);
265
1.27M
    if (test && !comp)
266
824k
        raw = true;
267
1.27M
    int method = raw ? M_PROPERTY_GET_STRING : M_PROPERTY_PRINT;
268
1.27M
    method = fixed_len ? M_PROPERTY_FIXED_LEN_PRINT : method;
269
270
1.27M
    char *s = NULL;
271
1.27M
    int r = m_property_do_bstr(prop_list, prop, method, &s, ctx);
272
1.27M
    bool skip;
273
1.27M
    if (comp) {
274
2.37k
        skip = ((s && bstr_equals0(comp_with, s)) != cond_yes);
275
1.27M
    } else if (test) {
276
824k
        skip = (!!s != cond_yes);
277
824k
    } else {
278
453k
        skip = !!s;
279
453k
        char *append = s;
280
453k
        if (!s && !silent_error && !raw)
281
11.5k
            append = (r == M_PROPERTY_UNAVAILABLE) ? "(unavailable)" : "(error)";
282
453k
        append_str(ret, ret_len, bstr0(append));
283
453k
    }
284
1.27M
    talloc_free(s);
285
1.27M
    return skip;
286
1.27M
}
287
288
char *m_properties_expand_string(const struct m_property *prop_list,
289
                                 const char *str0, void *ctx)
290
429k
{
291
429k
    char *ret = NULL;
292
429k
    int ret_len = 0;
293
429k
    bool skip = false;
294
429k
    int level = 0, skip_level = 0;
295
429k
    bstr str = bstr0(str0);
296
429k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
297
429k
    int n = 0;
298
429k
#endif
299
300
15.8M
    while (str.len) {
301
15.4M
        if (level > 0 && bstr_eatstart0(&str, "}")) {
302
1.27M
            if (skip && level <= skip_level)
303
845k
                skip = false;
304
1.27M
            level--;
305
14.1M
        } else if (bstr_startswith0(str, "${") && bstr_find0(str, "}") >= 0) {
306
1.28M
            str = bstr_cut(str, 2);
307
1.28M
            level++;
308
309
            // Assume ":" and "}" can't be part of the property name
310
            // => if ":" comes before "}", it must be for the fallback
311
1.28M
            int term_pos = bstrcspn(str, ":}");
312
1.28M
            bstr name = bstr_splice(str, 0, term_pos < 0 ? str.len : term_pos);
313
1.28M
            str = bstr_cut(str, term_pos);
314
1.28M
            bool have_fallback = bstr_eatstart0(&str, ":");
315
316
1.28M
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
317
1.28M
            if (n++ > 10)
318
693
                break;
319
1.28M
#endif
320
321
1.28M
            if (!skip) {
322
1.27M
                skip = expand_property(prop_list, &ret, &ret_len, name,
323
1.27M
                                       have_fallback, ctx);
324
1.27M
                if (skip)
325
846k
                    skip_level = level;
326
1.27M
            }
327
12.8M
        } else if (level == 0 && bstr_eatstart0(&str, "$>")) {
328
322
            append_str(&ret, &ret_len, str);
329
322
            break;
330
12.8M
        } else {
331
12.8M
            char c;
332
333
            // Other combinations, e.g. "$x", are added verbatim
334
12.8M
            if (bstr_eatstart0(&str, "$$")) {
335
2.65k
                c = '$';
336
12.8M
            } else if (bstr_eatstart0(&str, "$}")) {
337
2.24k
                c = '}';
338
12.8M
            } else {
339
12.8M
                c = str.start[0];
340
12.8M
                str = bstr_cut(str, 1);
341
12.8M
            }
342
343
12.8M
            if (!skip)
344
9.11M
                MP_TARRAY_APPEND(NULL, ret, ret_len, c);
345
12.8M
        }
346
15.4M
    }
347
348
429k
    MP_TARRAY_APPEND(NULL, ret, ret_len, '\0');
349
429k
    return ret;
350
429k
}
351
352
void m_properties_print_help_list(struct mp_log *log,
353
                                  const struct m_property *list)
354
1
{
355
1
    int count = 0;
356
357
1
    mp_info(log, "Name\n\n");
358
902
    for (int i = 0; list[i].name; i++) {
359
901
        const struct m_property *p = &list[i];
360
901
        mp_info(log, " %s\n", p->name);
361
901
        count++;
362
901
    }
363
1
    mp_info(log, "\nTotal: %d properties\n", count);
364
1
}
365
366
int m_property_bool_ro(int action, void* arg, bool var)
367
1.74k
{
368
1.74k
    switch (action) {
369
504
    case M_PROPERTY_GET:
370
504
        *(bool *)arg = !!var;
371
504
        return M_PROPERTY_OK;
372
527
    case M_PROPERTY_GET_TYPE:
373
527
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_BOOL};
374
527
        return M_PROPERTY_OK;
375
1.74k
    }
376
711
    return M_PROPERTY_NOT_IMPLEMENTED;
377
1.74k
}
378
379
int m_property_int_ro(int action, void *arg, int var)
380
893
{
381
893
    switch (action) {
382
268
    case M_PROPERTY_GET:
383
268
        *(int *)arg = var;
384
268
        return M_PROPERTY_OK;
385
280
    case M_PROPERTY_GET_TYPE:
386
280
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_INT};
387
280
        return M_PROPERTY_OK;
388
893
    }
389
345
    return M_PROPERTY_NOT_IMPLEMENTED;
390
893
}
391
392
int m_property_int64_ro(int action, void* arg, int64_t var)
393
1.22k
{
394
1.22k
    switch (action) {
395
304
    case M_PROPERTY_GET:
396
304
        *(int64_t *)arg = var;
397
304
        return M_PROPERTY_OK;
398
564
    case M_PROPERTY_GET_TYPE:
399
564
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_INT64};
400
564
        return M_PROPERTY_OK;
401
1.22k
    }
402
358
    return M_PROPERTY_NOT_IMPLEMENTED;
403
1.22k
}
404
405
int m_property_float_ro(int action, void *arg, float var)
406
0
{
407
0
    switch (action) {
408
0
    case M_PROPERTY_GET:
409
0
        *(float *)arg = var;
410
0
        return M_PROPERTY_OK;
411
0
    case M_PROPERTY_GET_TYPE:
412
0
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLOAT};
413
0
        return M_PROPERTY_OK;
414
0
    }
415
0
    return M_PROPERTY_NOT_IMPLEMENTED;
416
0
}
417
418
int m_property_double_ro(int action, void *arg, double var)
419
541
{
420
541
    switch (action) {
421
95
    case M_PROPERTY_GET:
422
95
        *(double *)arg = var;
423
95
        return M_PROPERTY_OK;
424
340
    case M_PROPERTY_GET_TYPE:
425
340
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_DOUBLE};
426
340
        return M_PROPERTY_OK;
427
541
    }
428
106
    return M_PROPERTY_NOT_IMPLEMENTED;
429
541
}
430
431
int m_property_strdup_ro(int action, void* arg, const char *var)
432
2.87M
{
433
2.87M
    if (!var)
434
129
        return M_PROPERTY_UNAVAILABLE;
435
2.87M
    switch (action) {
436
1.23M
    case M_PROPERTY_GET:
437
1.23M
        *(char **)arg = talloc_strdup(NULL, var);
438
1.23M
        return M_PROPERTY_OK;
439
1.23M
    case M_PROPERTY_GET_TYPE:
440
1.23M
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_STRING};
441
1.23M
        return M_PROPERTY_OK;
442
2.87M
    }
443
410k
    return M_PROPERTY_NOT_IMPLEMENTED;
444
2.87M
}
445
446
int m_property_read_sub_validate(void *ctx, struct m_property *prop,
447
                                 int action, void *arg)
448
4.35k
{
449
4.35k
    m_property_unkey(&action, &arg);
450
4.35k
    switch (action) {
451
325
    case M_PROPERTY_GET_TYPE:
452
325
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
453
325
        return M_PROPERTY_OK;
454
181
    case M_PROPERTY_GET:
455
364
    case M_PROPERTY_PRINT:
456
3.90k
    case M_PROPERTY_KEY_ACTION:
457
3.90k
        return M_PROPERTY_VALID;
458
132
    default:
459
132
        return M_PROPERTY_NOT_IMPLEMENTED;
460
4.35k
    };
461
0
}
462
463
// This allows you to make a list of values (like from a struct) available
464
// as a number of sub-properties. The property list is set up with the current
465
// property values on the stack before calling this function.
466
// This does not support write access.
467
int m_property_read_sub(const struct m_sub_property *props, int action, void *arg)
468
4.49M
{
469
4.49M
    m_property_unkey(&action, &arg);
470
4.49M
    switch (action) {
471
1.28M
    case M_PROPERTY_GET_TYPE:
472
1.28M
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
473
1.28M
        return M_PROPERTY_OK;
474
1.28M
    case M_PROPERTY_GET: {
475
1.28M
        struct mpv_node node;
476
1.28M
        node.format = MPV_FORMAT_NODE_MAP;
477
1.28M
        node.u.list = talloc_zero(NULL, mpv_node_list);
478
1.28M
        mpv_node_list *list = node.u.list;
479
5.14M
        for (int n = 0; props && props[n].name; n++) {
480
3.86M
            const struct m_sub_property *prop = &props[n];
481
3.86M
            if (prop->unavailable)
482
27.8k
                continue;
483
3.84M
            MP_TARRAY_GROW(list, list->values, list->num);
484
3.84M
            MP_TARRAY_GROW(list, list->keys, list->num);
485
3.84M
            mpv_node *val = &list->values[list->num];
486
3.84M
            if (m_option_get_node(&prop->type, list, val, (void*)&prop->value) < 0)
487
21
            {
488
21
                char *s = m_option_print(&prop->type, &prop->value);
489
21
                val->format = MPV_FORMAT_STRING;
490
21
                val->u.string = talloc_steal(list, s);
491
21
            }
492
3.84M
            list->keys[list->num] = (char *)prop->name;
493
3.84M
            list->num++;
494
3.84M
        }
495
1.28M
        *(struct mpv_node *)arg = node;
496
1.28M
        return M_PROPERTY_OK;
497
0
    }
498
644k
    case M_PROPERTY_PRINT: {
499
        // Output "something" - what it really should return is not yet decided.
500
        // It should probably be something that is easy to consume by slave
501
        // mode clients. (M_PROPERTY_PRINT on the other hand can return this
502
        // as human readable version just fine).
503
644k
        char *res = NULL;
504
2.58M
        for (int n = 0; props && props[n].name; n++) {
505
1.93M
            const struct m_sub_property *prop = &props[n];
506
1.93M
            if (prop->unavailable)
507
3.47k
                continue;
508
1.93M
            char *s = m_option_print(&prop->type, &prop->value);
509
1.93M
            ta_xasprintf_append(&res, "%s=%s\n", prop->name, s);
510
1.93M
            talloc_free(s);
511
1.93M
        }
512
644k
        *(char **)arg = res;
513
644k
        return M_PROPERTY_OK;
514
0
    }
515
9.59k
    case M_PROPERTY_KEY_ACTION: {
516
9.59k
        struct m_property_action_arg *ka = arg;
517
9.59k
        const struct m_sub_property *prop = NULL;
518
67.7k
        for (int n = 0; props && props[n].name; n++) {
519
66.9k
            if (strcmp(props[n].name, ka->key) == 0) {
520
8.85k
                prop = &props[n];
521
8.85k
                break;
522
8.85k
            }
523
66.9k
        }
524
9.59k
        if (!prop)
525
747
            return M_PROPERTY_UNKNOWN;
526
8.85k
        if (prop->unavailable)
527
41
            return M_PROPERTY_UNAVAILABLE;
528
8.80k
        switch (ka->action) {
529
1.55k
        case M_PROPERTY_GET: {
530
1.55k
            memset(ka->arg, 0, prop->type.type->size);
531
1.55k
            m_option_copy(&prop->type, ka->arg, &prop->value);
532
1.55k
            return M_PROPERTY_OK;
533
0
        }
534
5.57k
        case M_PROPERTY_GET_TYPE:
535
5.57k
            *(struct m_option *)ka->arg = prop->type;
536
5.57k
            return M_PROPERTY_OK;
537
8.80k
        }
538
8.80k
    }
539
4.49M
    }
540
1.28M
    return M_PROPERTY_NOT_IMPLEMENTED;
541
4.49M
}
542
543
544
// Make a list of items available as indexed sub-properties. E.g. you can access
545
// item 0 as "property/0", item 1 as "property/1", etc., where each of these
546
// properties is redirected to the get_item(0, ...), get_item(1, ...), callback.
547
// Additionally, the number of entries is made available as "property/count".
548
// action, arg: property access.
549
// count: number of items.
550
// get_item: callback to access a single item.
551
// ctx: userdata passed to get_item.
552
int m_property_read_list(int action, void *arg, int count,
553
                         m_get_item_cb get_item, void *ctx)
554
17.3k
{
555
17.3k
    m_property_unkey(&action, &arg);
556
17.3k
    switch (action) {
557
5.87k
    case M_PROPERTY_GET_TYPE:
558
5.87k
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
559
5.87k
        return M_PROPERTY_OK;
560
3.36k
    case M_PROPERTY_GET: {
561
3.36k
        struct mpv_node node;
562
3.36k
        node.format = MPV_FORMAT_NODE_ARRAY;
563
3.36k
        node.u.list = talloc_zero(NULL, mpv_node_list);
564
3.36k
        node.u.list->num = count;
565
3.36k
        node.u.list->values = talloc_array(node.u.list, mpv_node, count);
566
1.28M
        for (int n = 0; n < count; n++) {
567
1.27M
            struct mpv_node *sub = &node.u.list->values[n];
568
1.27M
            sub->format = MPV_FORMAT_NONE;
569
1.27M
            int r;
570
1.27M
            r = get_item(n, M_PROPERTY_GET_NODE, sub, ctx);
571
1.27M
            if (r == M_PROPERTY_NOT_IMPLEMENTED) {
572
1.27M
                struct m_option opt = {0};
573
1.27M
                r = get_item(n, M_PROPERTY_GET_TYPE, &opt, ctx);
574
1.27M
                if (r != M_PROPERTY_OK)
575
0
                    goto err;
576
1.27M
                union m_option_value val = m_option_value_default;
577
1.27M
                r = get_item(n, M_PROPERTY_GET, &val, ctx);
578
1.27M
                if (r != M_PROPERTY_OK)
579
0
                    goto err;
580
1.27M
                m_option_get_node(&opt, node.u.list, sub, &val);
581
1.27M
                m_option_free(&opt, &val);
582
1.27M
            err: ;
583
1.27M
            }
584
1.27M
        }
585
3.36k
        *(struct mpv_node *)arg = node;
586
3.36k
        return M_PROPERTY_OK;
587
3.36k
    }
588
1.51k
    case M_PROPERTY_PRINT: {
589
        // See m_property_read_sub() remarks.
590
1.51k
        char *res = NULL;
591
645k
        for (int n = 0; n < count; n++) {
592
643k
            char *s = NULL;
593
643k
            int r = get_item(n, M_PROPERTY_PRINT, &s, ctx);
594
643k
            if (r != M_PROPERTY_OK) {
595
0
                talloc_free(res);
596
0
                return r;
597
0
            }
598
643k
            ta_xasprintf_append(&res, "%d: %s\n", n, s);
599
643k
            talloc_free(s);
600
643k
        }
601
1.51k
        *(char **)arg = res;
602
1.51k
        return M_PROPERTY_OK;
603
1.51k
    }
604
5.14k
    case M_PROPERTY_KEY_ACTION: {
605
5.14k
        struct m_property_action_arg *ka = arg;
606
5.14k
        if (strcmp(ka->key, "count") == 0) {
607
699
            switch (ka->action) {
608
418
            case M_PROPERTY_GET_TYPE: {
609
418
                struct m_option opt = {.type = CONF_TYPE_INT};
610
418
                *(struct m_option *)ka->arg = opt;
611
418
                return M_PROPERTY_OK;
612
0
            }
613
154
            case M_PROPERTY_GET:
614
154
                *(int *)ka->arg = MPMAX(0, count);
615
154
                return M_PROPERTY_OK;
616
699
            }
617
127
            return M_PROPERTY_NOT_IMPLEMENTED;
618
699
        }
619
        // This is expected of the form "123" or "123/rest"
620
4.44k
        char *end;
621
4.44k
        long int item = strtol(ka->key, &end, 10);
622
        // not a number, trailing characters, etc.
623
4.44k
        if (end == ka->key || (end[0] == '/' && !end[1]))
624
882
            return M_PROPERTY_UNKNOWN;
625
3.56k
        if (item < 0 || item >= count)
626
1.09k
            return M_PROPERTY_UNKNOWN;
627
2.46k
        if (*end) {
628
            // Sub-path
629
1.99k
            struct m_property_action_arg n_ka = *ka;
630
1.99k
            n_ka.key = end + 1;
631
1.99k
            return get_item(item, M_PROPERTY_KEY_ACTION, &n_ka, ctx);
632
1.99k
        } else {
633
            // Direct query
634
475
            return get_item(item, ka->action, ka->arg, ctx);
635
475
        }
636
2.46k
    }
637
17.3k
    }
638
1.42k
    return M_PROPERTY_NOT_IMPLEMENTED;
639
17.3k
}