/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 | } |