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