Coverage Report

Created: 2025-12-11 06:55

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.50M
{
66
61.0M
    for (int n = 0; list && list[n].name; n++) {
67
61.0M
        if (strcmp(list[n].name, name) == 0)
68
3.50M
            return (struct m_property *)&list[n];
69
61.0M
    }
70
5.71k
    return NULL;
71
3.50M
}
72
73
static int do_action(const struct m_property *prop_list, const char *name,
74
                     int action, void *arg, void *ctx)
75
3.50M
{
76
3.50M
    struct m_property *prop;
77
3.50M
    struct m_property_action_arg ka;
78
3.50M
    const char *sep = strchr(name, '/');
79
3.50M
    if (sep && sep[1]) {
80
18.7k
        char base[128];
81
18.7k
        snprintf(base, sizeof(base), "%.*s", (int)(sep - name), name);
82
18.7k
        prop = m_property_list_find(prop_list, base);
83
18.7k
        ka = (struct m_property_action_arg) {
84
18.7k
            .key = sep + 1,
85
18.7k
            .action = action,
86
18.7k
            .arg = arg,
87
18.7k
        };
88
18.7k
        action = M_PROPERTY_KEY_ACTION;
89
18.7k
        arg = &ka;
90
18.7k
    } else
91
3.48M
        prop = m_property_list_find(prop_list, name);
92
3.50M
    if (!prop)
93
5.71k
        return M_PROPERTY_UNKNOWN;
94
3.50M
    return prop->call(ctx, prop, action, arg);
95
3.50M
}
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.50M
{
101
1.50M
    union m_option_value val = m_option_value_default;
102
1.50M
    int r;
103
104
1.50M
    struct m_option opt = {0};
105
1.50M
    r = do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx);
106
1.50M
    if (r <= 0)
107
15.8k
        return r;
108
1.49M
    mp_assert(opt.type);
109
110
1.49M
    switch (action) {
111
3.12k
    case M_PROPERTY_FIXED_LEN_PRINT:
112
501k
    case M_PROPERTY_PRINT: {
113
501k
        if ((r = do_action(prop_list, name, action, arg, ctx)) >= 0)
114
6.09k
            return r;
115
        // Fallback to m_option
116
495k
        if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
117
400
            return r;
118
495k
        char *str = m_option_pretty_print(&opt, &val, action == M_PROPERTY_FIXED_LEN_PRINT);
119
495k
        m_option_free(&opt, &val);
120
495k
        *(char **)arg = str;
121
495k
        return str != NULL;
122
495k
    }
123
973k
    case M_PROPERTY_GET_STRING: {
124
973k
        if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
125
184
            return r;
126
973k
        char *str = m_option_print(&opt, &val);
127
973k
        m_option_free(&opt, &val);
128
973k
        *(char **)arg = str;
129
973k
        return str != NULL;
130
973k
    }
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
973k
    }
135
0
    case M_PROPERTY_MULTIPLY: {
136
0
        return m_property_multiply(log, prop_list, name, *(double *)arg, ctx);
137
973k
    }
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
361
    case M_PROPERTY_SET: {
169
361
        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.9k
    case M_PROPERTY_SET_NODE: {
190
13.9k
        if (!log)
191
0
            return M_PROPERTY_ERROR;
192
13.9k
        if ((r = do_action(prop_list, name, M_PROPERTY_SET_NODE, arg, ctx)) !=
193
13.9k
            M_PROPERTY_NOT_IMPLEMENTED)
194
1.01k
            return r;
195
12.9k
        int err = m_option_set_node_or_string(log, &opt, bstr0(name), &val, arg);
196
12.9k
        if (err == M_OPT_UNKNOWN) {
197
226
            r = M_PROPERTY_NOT_IMPLEMENTED;
198
12.7k
        } else if (err < 0) {
199
1.84k
            r = M_PROPERTY_INVALID_FORMAT;
200
10.9k
        } else {
201
10.9k
            r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx);
202
10.9k
        }
203
12.9k
        m_option_free(&opt, &val);
204
12.9k
        return r;
205
13.9k
    }
206
2.72k
    default:
207
2.72k
        return do_action(prop_list, name, action, arg, ctx);
208
1.49M
    }
209
1.49M
}
210
211
bool m_property_split_path(const char *path, bstr *prefix, char **rem)
212
21.9k
{
213
21.9k
    char *next = strchr(path, '/');
214
21.9k
    if (next) {
215
18.7k
        *prefix = bstr_splice(bstr0(path), 0, next - path);
216
18.7k
        *rem = next + 1;
217
18.7k
        return true;
218
18.7k
    } else {
219
3.21k
        *prefix = bstr0(path);
220
3.21k
        *rem = "";
221
3.21k
        return false;
222
3.21k
    }
223
21.9k
}
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
3.88M
{
229
3.88M
    if (*action == M_PROPERTY_KEY_ACTION) {
230
14.1k
        struct m_property_action_arg *ka = *arg;
231
14.1k
        if (!ka->key[0]) {
232
1.76k
            *action = ka->action;
233
1.76k
            *arg = ka->arg;
234
1.76k
        }
235
14.1k
    }
236
3.88M
}
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.48M
{
241
1.48M
    char *name0 = bstrdup0(NULL, name);
242
1.48M
    int ret = m_property_do(NULL, prop_list, name0, action, arg, ctx);
243
1.48M
    talloc_free(name0);
244
1.48M
    return ret;
245
1.48M
}
246
247
static void append_str(char **s, int *len, bstr append)
248
513k
{
249
513k
    MP_TARRAY_GROW(NULL, *s, *len + append.len);
250
513k
    if (append.len)
251
508k
        memcpy(*s + *len, append.start, append.len);
252
513k
    *len = *len + append.len;
253
513k
}
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.48M
{
258
1.48M
    bool cond_yes = bstr_eatstart0(&prop, "?");
259
1.48M
    bool cond_no = !cond_yes && bstr_eatstart0(&prop, "!");
260
1.48M
    bool test = cond_yes || cond_no;
261
1.48M
    bool raw = bstr_eatstart0(&prop, "=");
262
1.48M
    bool fixed_len = !raw && bstr_eatstart0(&prop, ">");
263
1.48M
    bstr comp_with = {0};
264
1.48M
    bool comp = test && bstr_split_tok(prop, "==", &prop, &comp_with);
265
1.48M
    if (test && !comp)
266
972k
        raw = true;
267
1.48M
    int method = raw ? M_PROPERTY_GET_STRING : M_PROPERTY_PRINT;
268
1.48M
    method = fixed_len ? M_PROPERTY_FIXED_LEN_PRINT : method;
269
270
1.48M
    char *s = NULL;
271
1.48M
    int r = m_property_do_bstr(prop_list, prop, method, &s, ctx);
272
1.48M
    bool skip;
273
1.48M
    if (comp) {
274
970
        skip = ((s && bstr_equals0(comp_with, s)) != cond_yes);
275
1.48M
    } else if (test) {
276
972k
        skip = (!!s != cond_yes);
277
972k
    } else {
278
512k
        skip = !!s;
279
512k
        char *append = s;
280
512k
        if (!s && !silent_error && !raw)
281
7.16k
            append = (r == M_PROPERTY_UNAVAILABLE) ? "(unavailable)" : "(error)";
282
512k
        append_str(ret, ret_len, bstr0(append));
283
512k
    }
284
1.48M
    talloc_free(s);
285
1.48M
    return skip;
286
1.48M
}
287
288
char *m_properties_expand_string(const struct m_property *prop_list,
289
                                 const char *str0, void *ctx)
290
497k
{
291
497k
    char *ret = NULL;
292
497k
    int ret_len = 0;
293
497k
    bool skip = false;
294
497k
    int level = 0, skip_level = 0;
295
497k
    bstr str = bstr0(str0);
296
497k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
297
497k
    int n = 0;
298
497k
#endif
299
300
15.4M
    while (str.len) {
301
14.9M
        if (level > 0 && bstr_eatstart0(&str, "}")) {
302
1.48M
            if (skip && level <= skip_level)
303
987k
                skip = false;
304
1.48M
            level--;
305
13.4M
        } else if (bstr_startswith0(str, "${") && bstr_find0(str, "}") >= 0) {
306
1.48M
            str = bstr_cut(str, 2);
307
1.48M
            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.48M
            int term_pos = bstrcspn(str, ":}");
312
1.48M
            bstr name = bstr_splice(str, 0, term_pos < 0 ? str.len : term_pos);
313
1.48M
            str = bstr_cut(str, term_pos);
314
1.48M
            bool have_fallback = bstr_eatstart0(&str, ":");
315
316
1.48M
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
317
1.48M
            if (n++ > 10)
318
261
                break;
319
1.48M
#endif
320
321
1.48M
            if (!skip) {
322
1.48M
                skip = expand_property(prop_list, &ret, &ret_len, name,
323
1.48M
                                       have_fallback, ctx);
324
1.48M
                if (skip)
325
987k
                    skip_level = level;
326
1.48M
            }
327
12.0M
        } else if (level == 0 && bstr_eatstart0(&str, "$>")) {
328
162
            append_str(&ret, &ret_len, str);
329
162
            break;
330
12.0M
        } else {
331
12.0M
            char c;
332
333
            // Other combinations, e.g. "$x", are added verbatim
334
12.0M
            if (bstr_eatstart0(&str, "$$")) {
335
1.24k
                c = '$';
336
12.0M
            } else if (bstr_eatstart0(&str, "$}")) {
337
842
                c = '}';
338
11.9M
            } else {
339
11.9M
                c = str.start[0];
340
11.9M
                str = bstr_cut(str, 1);
341
11.9M
            }
342
343
12.0M
            if (!skip)
344
8.42M
                MP_TARRAY_APPEND(NULL, ret, ret_len, c);
345
12.0M
        }
346
14.9M
    }
347
348
497k
    MP_TARRAY_APPEND(NULL, ret, ret_len, '\0');
349
497k
    return ret;
350
497k
}
351
352
void m_properties_print_help_list(struct mp_log *log,
353
                                  const struct m_property *list)
354
0
{
355
0
    int count = 0;
356
357
0
    mp_info(log, "Name\n\n");
358
0
    for (int i = 0; list[i].name; i++) {
359
0
        const struct m_property *p = &list[i];
360
0
        mp_info(log, " %s\n", p->name);
361
0
        count++;
362
0
    }
363
0
    mp_info(log, "\nTotal: %d properties\n", count);
364
0
}
365
366
int m_property_bool_ro(int action, void* arg, bool var)
367
1.41k
{
368
1.41k
    switch (action) {
369
393
    case M_PROPERTY_GET:
370
393
        *(bool *)arg = !!var;
371
393
        return M_PROPERTY_OK;
372
415
    case M_PROPERTY_GET_TYPE:
373
415
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_BOOL};
374
415
        return M_PROPERTY_OK;
375
1.41k
    }
376
605
    return M_PROPERTY_NOT_IMPLEMENTED;
377
1.41k
}
378
379
int m_property_int_ro(int action, void *arg, int var)
380
821
{
381
821
    switch (action) {
382
241
    case M_PROPERTY_GET:
383
241
        *(int *)arg = var;
384
241
        return M_PROPERTY_OK;
385
253
    case M_PROPERTY_GET_TYPE:
386
253
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_INT};
387
253
        return M_PROPERTY_OK;
388
821
    }
389
327
    return M_PROPERTY_NOT_IMPLEMENTED;
390
821
}
391
392
int m_property_int64_ro(int action, void* arg, int64_t var)
393
805
{
394
805
    switch (action) {
395
238
    case M_PROPERTY_GET:
396
238
        *(int64_t *)arg = var;
397
238
        return M_PROPERTY_OK;
398
272
    case M_PROPERTY_GET_TYPE:
399
272
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_INT64};
400
272
        return M_PROPERTY_OK;
401
805
    }
402
295
    return M_PROPERTY_NOT_IMPLEMENTED;
403
805
}
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
425
{
420
425
    switch (action) {
421
52
    case M_PROPERTY_GET:
422
52
        *(double *)arg = var;
423
52
        return M_PROPERTY_OK;
424
277
    case M_PROPERTY_GET_TYPE:
425
277
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_DOUBLE};
426
277
        return M_PROPERTY_OK;
427
425
    }
428
96
    return M_PROPERTY_NOT_IMPLEMENTED;
429
425
}
430
431
int m_property_strdup_ro(int action, void* arg, const char *var)
432
3.39M
{
433
3.39M
    if (!var)
434
116
        return M_PROPERTY_UNAVAILABLE;
435
3.39M
    switch (action) {
436
1.45M
    case M_PROPERTY_GET:
437
1.45M
        *(char **)arg = talloc_strdup(NULL, var);
438
1.45M
        return M_PROPERTY_OK;
439
1.45M
    case M_PROPERTY_GET_TYPE:
440
1.45M
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_STRING};
441
1.45M
        return M_PROPERTY_OK;
442
3.39M
    }
443
485k
    return M_PROPERTY_NOT_IMPLEMENTED;
444
3.39M
}
445
446
int m_property_read_sub_validate(void *ctx, struct m_property *prop,
447
                                 int action, void *arg)
448
3.22k
{
449
3.22k
    m_property_unkey(&action, &arg);
450
3.22k
    switch (action) {
451
200
    case M_PROPERTY_GET_TYPE:
452
200
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
453
200
        return M_PROPERTY_OK;
454
108
    case M_PROPERTY_GET:
455
223
    case M_PROPERTY_PRINT:
456
2.95k
    case M_PROPERTY_KEY_ACTION:
457
2.95k
        return M_PROPERTY_VALID;
458
67
    default:
459
67
        return M_PROPERTY_NOT_IMPLEMENTED;
460
3.22k
    };
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
3.86M
{
469
3.86M
    m_property_unkey(&action, &arg);
470
3.86M
    switch (action) {
471
1.12M
    case M_PROPERTY_GET_TYPE:
472
1.12M
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
473
1.12M
        return M_PROPERTY_OK;
474
1.12M
    case M_PROPERTY_GET: {
475
1.12M
        struct mpv_node node;
476
1.12M
        node.format = MPV_FORMAT_NODE_MAP;
477
1.12M
        node.u.list = talloc_zero(NULL, mpv_node_list);
478
1.12M
        mpv_node_list *list = node.u.list;
479
4.53M
        for (int n = 0; props && props[n].name; n++) {
480
3.41M
            const struct m_sub_property *prop = &props[n];
481
3.41M
            if (prop->unavailable)
482
58.5k
                continue;
483
3.35M
            MP_TARRAY_GROW(list, list->values, list->num);
484
3.35M
            MP_TARRAY_GROW(list, list->keys, list->num);
485
3.35M
            mpv_node *val = &list->values[list->num];
486
3.35M
            if (m_option_get_node(&prop->type, list, val, (void*)&prop->value) < 0)
487
18
            {
488
18
                char *s = m_option_print(&prop->type, &prop->value);
489
18
                val->format = MPV_FORMAT_STRING;
490
18
                val->u.string = talloc_steal(list, s);
491
18
            }
492
3.35M
            list->keys[list->num] = (char *)prop->name;
493
3.35M
            list->num++;
494
3.35M
        }
495
1.12M
        *(struct mpv_node *)arg = node;
496
1.12M
        return M_PROPERTY_OK;
497
0
    }
498
499k
    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
499k
        char *res = NULL;
504
2.00M
        for (int n = 0; props && props[n].name; n++) {
505
1.50M
            const struct m_sub_property *prop = &props[n];
506
1.50M
            if (prop->unavailable)
507
2.22k
                continue;
508
1.50M
            char *s = m_option_print(&prop->type, &prop->value);
509
1.50M
            ta_xasprintf_append(&res, "%s=%s\n", prop->name, s);
510
1.50M
            talloc_free(s);
511
1.50M
        }
512
499k
        *(char **)arg = res;
513
499k
        return M_PROPERTY_OK;
514
0
    }
515
6.12k
    case M_PROPERTY_KEY_ACTION: {
516
6.12k
        struct m_property_action_arg *ka = arg;
517
6.12k
        const struct m_sub_property *prop = NULL;
518
29.3k
        for (int n = 0; props && props[n].name; n++) {
519
28.7k
            if (strcmp(props[n].name, ka->key) == 0) {
520
5.48k
                prop = &props[n];
521
5.48k
                break;
522
5.48k
            }
523
28.7k
        }
524
6.12k
        if (!prop)
525
648
            return M_PROPERTY_UNKNOWN;
526
5.48k
        if (prop->unavailable)
527
46
            return M_PROPERTY_UNAVAILABLE;
528
5.43k
        switch (ka->action) {
529
1.02k
        case M_PROPERTY_GET: {
530
1.02k
            memset(ka->arg, 0, prop->type.type->size);
531
1.02k
            m_option_copy(&prop->type, ka->arg, &prop->value);
532
1.02k
            return M_PROPERTY_OK;
533
0
        }
534
3.36k
        case M_PROPERTY_GET_TYPE:
535
3.36k
            *(struct m_option *)ka->arg = prop->type;
536
3.36k
            return M_PROPERTY_OK;
537
5.43k
        }
538
5.43k
    }
539
3.86M
    }
540
1.12M
    return M_PROPERTY_NOT_IMPLEMENTED;
541
3.86M
}
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
13.7k
{
555
13.7k
    m_property_unkey(&action, &arg);
556
13.7k
    switch (action) {
557
4.89k
    case M_PROPERTY_GET_TYPE:
558
4.89k
        *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
559
4.89k
        return M_PROPERTY_OK;
560
3.07k
    case M_PROPERTY_GET: {
561
3.07k
        struct mpv_node node;
562
3.07k
        node.format = MPV_FORMAT_NODE_ARRAY;
563
3.07k
        node.u.list = talloc_zero(NULL, mpv_node_list);
564
3.07k
        node.u.list->num = count;
565
3.07k
        node.u.list->values = talloc_array(node.u.list, mpv_node, count);
566
1.12M
        for (int n = 0; n < count; n++) {
567
1.11M
            struct mpv_node *sub = &node.u.list->values[n];
568
1.11M
            sub->format = MPV_FORMAT_NONE;
569
1.11M
            int r;
570
1.11M
            r = get_item(n, M_PROPERTY_GET_NODE, sub, ctx);
571
1.11M
            if (r == M_PROPERTY_NOT_IMPLEMENTED) {
572
1.11M
                struct m_option opt = {0};
573
1.11M
                r = get_item(n, M_PROPERTY_GET_TYPE, &opt, ctx);
574
1.11M
                if (r != M_PROPERTY_OK)
575
0
                    goto err;
576
1.11M
                union m_option_value val = m_option_value_default;
577
1.11M
                r = get_item(n, M_PROPERTY_GET, &val, ctx);
578
1.11M
                if (r != M_PROPERTY_OK)
579
0
                    goto err;
580
1.11M
                m_option_get_node(&opt, node.u.list, sub, &val);
581
1.11M
                m_option_free(&opt, &val);
582
1.11M
            err: ;
583
1.11M
            }
584
1.11M
        }
585
3.07k
        *(struct mpv_node *)arg = node;
586
3.07k
        return M_PROPERTY_OK;
587
3.07k
    }
588
1.11k
    case M_PROPERTY_PRINT: {
589
        // See m_property_read_sub() remarks.
590
1.11k
        char *res = NULL;
591
499k
        for (int n = 0; n < count; n++) {
592
498k
            char *s = NULL;
593
498k
            int r = get_item(n, M_PROPERTY_PRINT, &s, ctx);
594
498k
            if (r != M_PROPERTY_OK) {
595
0
                talloc_free(res);
596
0
                return r;
597
0
            }
598
498k
            ta_xasprintf_append(&res, "%d: %s\n", n, s);
599
498k
            talloc_free(s);
600
498k
        }
601
1.11k
        *(char **)arg = res;
602
1.11k
        return M_PROPERTY_OK;
603
1.11k
    }
604
3.51k
    case M_PROPERTY_KEY_ACTION: {
605
3.51k
        struct m_property_action_arg *ka = arg;
606
3.51k
        if (strcmp(ka->key, "count") == 0) {
607
476
            switch (ka->action) {
608
268
            case M_PROPERTY_GET_TYPE: {
609
268
                struct m_option opt = {.type = CONF_TYPE_INT};
610
268
                *(struct m_option *)ka->arg = opt;
611
268
                return M_PROPERTY_OK;
612
0
            }
613
100
            case M_PROPERTY_GET:
614
100
                *(int *)ka->arg = MPMAX(0, count);
615
100
                return M_PROPERTY_OK;
616
476
            }
617
108
            return M_PROPERTY_NOT_IMPLEMENTED;
618
476
        }
619
        // This is expected of the form "123" or "123/rest"
620
3.04k
        char *end;
621
3.04k
        long int item = strtol(ka->key, &end, 10);
622
        // not a number, trailing characters, etc.
623
3.04k
        if (end == ka->key || (end[0] == '/' && !end[1]))
624
782
            return M_PROPERTY_UNKNOWN;
625
2.26k
        if (item < 0 || item >= count)
626
1.13k
            return M_PROPERTY_UNKNOWN;
627
1.12k
        if (*end) {
628
            // Sub-path
629
659
            struct m_property_action_arg n_ka = *ka;
630
659
            n_ka.key = end + 1;
631
659
            return get_item(item, M_PROPERTY_KEY_ACTION, &n_ka, ctx);
632
659
        } else {
633
            // Direct query
634
467
            return get_item(item, ka->action, ka->arg, ctx);
635
467
        }
636
1.12k
    }
637
13.7k
    }
638
1.18k
    return M_PROPERTY_NOT_IMPLEMENTED;
639
13.7k
}