1 /*****************************************************************************
2 * variables.c: routines for object variables handling
3 *****************************************************************************
4 * Copyright (C) 2002-2009 VLC authors and VideoLAN
5 * $Id: 810c8ffc819365cce4c6889885055315d9fcb337 $
6 *
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
23
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_SEARCH_H
32 # include <search.h>
33 #endif
34 #include <assert.h>
35 #include <float.h>
36 #include <math.h>
37 #include <limits.h>
38
39 #include <vlc_common.h>
40 #include <vlc_arrays.h>
41 #include <vlc_charset.h>
42 #include "libvlc.h"
43 #include "variables.h"
44 #include "config/configuration.h"
45
46 typedef struct callback_entry_t
47 {
48 union
49 {
50 vlc_callback_t pf_value_callback;
51 vlc_list_callback_t pf_list_callback;
52 void * p_callback;
53 };
54 void * p_data;
55 } callback_entry_t;
56
57 typedef struct variable_ops_t
58 {
59 int (*pf_cmp) ( vlc_value_t, vlc_value_t );
60 void (*pf_dup) ( vlc_value_t * );
61 void (*pf_free) ( vlc_value_t * );
62 } variable_ops_t;
63
64 typedef struct callback_table_t
65 {
66 int i_entries;
67 callback_entry_t * p_entries;
68 } callback_table_t;
69
70 /**
71 * The structure describing a variable.
72 * \note vlc_value_t is the common union for variable values
73 */
74 struct variable_t
75 {
76 char * psz_name; /**< The variable unique name (must be first) */
77
78 /** The variable's exported value */
79 vlc_value_t val;
80
81 /** The variable display name, mainly for use by the interfaces */
82 char * psz_text;
83
84 const variable_ops_t *ops;
85
86 int i_type; /**< The type of the variable */
87 unsigned i_usage; /**< Reference count */
88
89 /** If the variable has min/max/step values */
90 vlc_value_t min, max, step;
91
92 /** List of choices */
93 vlc_list_t choices;
94 /** List of friendly names for the choices */
95 vlc_list_t choices_text;
96
97 /** Set to TRUE if the variable is in a callback */
98 bool b_incallback;
99
100 /** Registered value callbacks */
101 callback_table_t value_callbacks;
102 /** Registered list callbacks */
103 callback_table_t list_callbacks;
104 };
105
CmpBool(vlc_value_t v,vlc_value_t w)106 static int CmpBool( vlc_value_t v, vlc_value_t w )
107 {
108 return v.b_bool ? w.b_bool ? 0 : 1 : w.b_bool ? -1 : 0;
109 }
110
CmpInt(vlc_value_t v,vlc_value_t w)111 static int CmpInt( vlc_value_t v, vlc_value_t w )
112 {
113 return v.i_int == w.i_int ? 0 : v.i_int > w.i_int ? 1 : -1;
114 }
115
CmpString(vlc_value_t v,vlc_value_t w)116 static int CmpString( vlc_value_t v, vlc_value_t w )
117 {
118 if( !v.psz_string )
119 return !w.psz_string ? 0 : -1;
120 else
121 return !w.psz_string ? 1 : strcmp( v.psz_string, w.psz_string );
122 }
CmpFloat(vlc_value_t v,vlc_value_t w)123 static int CmpFloat( vlc_value_t v, vlc_value_t w ) { return v.f_float == w.f_float ? 0 : v.f_float > w.f_float ? 1 : -1; }
CmpAddress(vlc_value_t v,vlc_value_t w)124 static int CmpAddress( vlc_value_t v, vlc_value_t w ) { return v.p_address == w.p_address ? 0 : v.p_address > w.p_address ? 1 : -1; }
125
DupDummy(vlc_value_t * p_val)126 static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
DupString(vlc_value_t * p_val)127 static void DupString( vlc_value_t *p_val )
128 {
129 p_val->psz_string = strdup( p_val->psz_string ? p_val->psz_string : "" );
130 }
131
FreeDummy(vlc_value_t * p_val)132 static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
FreeString(vlc_value_t * p_val)133 static void FreeString( vlc_value_t *p_val ) { free( p_val->psz_string ); }
134
135 static const struct variable_ops_t
136 void_ops = { NULL, DupDummy, FreeDummy, },
137 addr_ops = { CmpAddress, DupDummy, FreeDummy, },
138 bool_ops = { CmpBool, DupDummy, FreeDummy, },
139 float_ops = { CmpFloat, DupDummy, FreeDummy, },
140 int_ops = { CmpInt, DupDummy, FreeDummy, },
141 string_ops = { CmpString, DupString, FreeString, },
142 coords_ops = { NULL, DupDummy, FreeDummy, };
143
varcmp(const void * a,const void * b)144 static int varcmp( const void *a, const void *b )
145 {
146 const variable_t *va = a, *vb = b;
147
148 /* psz_name must be first */
149 assert( va == (const void *)&va->psz_name );
150 return strcmp( va->psz_name, vb->psz_name );
151 }
152
Lookup(vlc_object_t * obj,const char * psz_name)153 static variable_t *Lookup( vlc_object_t *obj, const char *psz_name )
154 {
155 vlc_object_internals_t *priv = vlc_internals( obj );
156 variable_t **pp_var;
157
158 vlc_mutex_lock(&priv->var_lock);
159 pp_var = tfind( &psz_name, &priv->var_root, varcmp );
160 return (pp_var != NULL) ? *pp_var : NULL;
161 }
162
Destroy(variable_t * p_var)163 static void Destroy( variable_t *p_var )
164 {
165 p_var->ops->pf_free( &p_var->val );
166 if( p_var->choices.i_count )
167 {
168 for( int i = 0 ; i < p_var->choices.i_count ; i++ )
169 {
170 p_var->ops->pf_free( &p_var->choices.p_values[i] );
171 free( p_var->choices_text.p_values[i].psz_string );
172 }
173 free( p_var->choices.p_values );
174 free( p_var->choices_text.p_values );
175 }
176
177 free( p_var->psz_name );
178 free( p_var->psz_text );
179 free( p_var->value_callbacks.p_entries );
180 free( p_var );
181 }
182
183 /**
184 * Adjusts a value to fit the constraints for a certain variable:
185 * - If the value is lower than the minimum, use the minimum.
186 * - If the value is higher than the maximum, use the maximum.
187 * - If the variable has steps, round the value to the nearest step.
188 */
CheckValue(variable_t * var,vlc_value_t * val)189 static void CheckValue(variable_t *var, vlc_value_t *val)
190 {
191 /* Check that our variable is within the bounds */
192 switch (var->i_type & VLC_VAR_TYPE)
193 {
194 case VLC_VAR_INTEGER:
195 if (val->i_int < var->min.i_int)
196 val->i_int = var->min.i_int;
197 if (val->i_int > var->max.i_int)
198 val->i_int = var->max.i_int;
199 if (var->step.i_int != 0 && (val->i_int % var->step.i_int))
200 {
201 if (val->i_int > 0)
202 val->i_int = (val->i_int + (var->step.i_int / 2))
203 / var->step.i_int * var->step.i_int;
204 else
205 val->i_int = (val->i_int - (var->step.i_int / 2))
206 / var->step.i_int * var->step.i_int;
207 }
208 break;
209
210 case VLC_VAR_FLOAT:
211 if (isless(val->f_float, var->min.f_float))
212 val->f_float = var->min.f_float;
213 if (isgreater(val->f_float, var->max.f_float))
214 val->f_float = var->max.f_float;
215 if (var->step.f_float != 0.f)
216 val->f_float = var->step.f_float
217 * roundf(val->f_float / var->step.f_float);
218 break;
219 }
220 }
221
222 /**
223 * Waits until the variable is inactive (i.e. not executing a callback)
224 */
WaitUnused(vlc_object_t * obj,variable_t * var)225 static void WaitUnused(vlc_object_t *obj, variable_t *var)
226 {
227 vlc_object_internals_t *priv = vlc_internals(obj);
228
229 mutex_cleanup_push(&priv->var_lock);
230 while (var->b_incallback)
231 vlc_cond_wait(&priv->var_wait, &priv->var_lock);
232 vlc_cleanup_pop();
233 }
234
TriggerCallback(vlc_object_t * obj,variable_t * var,const char * name,vlc_value_t prev)235 static void TriggerCallback(vlc_object_t *obj, variable_t *var,
236 const char *name, vlc_value_t prev)
237 {
238 assert(obj != NULL);
239
240 size_t count = var->value_callbacks.i_entries;
241 if (count == 0)
242 return;
243
244 callback_entry_t *entries = var->value_callbacks.p_entries;
245 vlc_object_internals_t *priv = vlc_internals(obj);
246
247 assert(!var->b_incallback);
248 var->b_incallback = true;
249 vlc_mutex_unlock(&priv->var_lock);
250
251 for (size_t i = 0; i < count; i++)
252 entries[i].pf_value_callback(obj, name, prev, var->val,
253 entries[i].p_data);
254
255 vlc_mutex_lock(&priv->var_lock);
256 var->b_incallback = false;
257 vlc_cond_broadcast(&priv->var_wait);
258 }
259
TriggerListCallback(vlc_object_t * obj,variable_t * var,const char * name,int action,vlc_value_t * val)260 static void TriggerListCallback(vlc_object_t *obj, variable_t *var,
261 const char *name, int action, vlc_value_t *val)
262 {
263 assert(obj != NULL);
264
265 size_t count = var->list_callbacks.i_entries;
266 if (count == 0)
267 return;
268
269 callback_entry_t *entries = var->list_callbacks.p_entries;
270 vlc_object_internals_t *priv = vlc_internals(obj);
271
272 assert(!var->b_incallback);
273 var->b_incallback = true;
274 vlc_mutex_unlock(&priv->var_lock);
275
276 for (size_t i = 0; i < count; i++)
277 entries[i].pf_list_callback(obj, name, action, val,
278 entries[i].p_data);
279
280 vlc_mutex_lock(&priv->var_lock);
281 var->b_incallback = false;
282 vlc_cond_broadcast(&priv->var_wait);
283 }
284
285 #undef var_Create
286 /**
287 * Initialize a vlc variable
288 *
289 * We hash the given string and insert it into the sorted list. The insertion
290 * may require slow memory copies, but think about what we gain in the log(n)
291 * lookup phase when setting/getting the variable value!
292 *
293 * \param p_this The object in which to create the variable
294 * \param psz_name The name of the variable
295 * \param i_type The variables type. Must be one of \ref var_type combined with
296 * zero or more \ref var_flags
297 */
var_Create(vlc_object_t * p_this,const char * psz_name,int i_type)298 int var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
299 {
300 assert( p_this );
301
302 variable_t *p_var = calloc( 1, sizeof( *p_var ) );
303 if( p_var == NULL )
304 return VLC_ENOMEM;
305
306 p_var->psz_name = strdup( psz_name );
307 p_var->psz_text = NULL;
308
309 p_var->i_type = i_type & ~VLC_VAR_DOINHERIT;
310
311 p_var->i_usage = 1;
312
313 p_var->choices.i_count = 0;
314 p_var->choices.p_values = NULL;
315 p_var->choices_text.i_count = 0;
316 p_var->choices_text.p_values = NULL;
317
318 p_var->b_incallback = false;
319 p_var->value_callbacks = (callback_table_t){ 0, NULL };
320
321 /* Always initialize the variable, even if it is a list variable; this
322 * will lead to errors if the variable is not initialized, but it will
323 * not cause crashes in the variable handling. */
324 switch( i_type & VLC_VAR_CLASS )
325 {
326 case VLC_VAR_BOOL:
327 p_var->ops = &bool_ops;
328 p_var->val.b_bool = false;
329 break;
330 case VLC_VAR_INTEGER:
331 p_var->ops = &int_ops;
332 p_var->val.i_int = 0;
333 p_var->min.i_int = INT64_MIN;
334 p_var->max.i_int = INT64_MAX;
335 break;
336 case VLC_VAR_STRING:
337 p_var->ops = &string_ops;
338 p_var->val.psz_string = NULL;
339 break;
340 case VLC_VAR_FLOAT:
341 p_var->ops = &float_ops;
342 p_var->val.f_float = 0.f;
343 p_var->min.f_float = -FLT_MAX;
344 p_var->max.f_float = FLT_MAX;
345 break;
346 case VLC_VAR_COORDS:
347 p_var->ops = &coords_ops;
348 p_var->val.coords.x = p_var->val.coords.y = 0;
349 break;
350 case VLC_VAR_ADDRESS:
351 p_var->ops = &addr_ops;
352 p_var->val.p_address = NULL;
353 break;
354 case VLC_VAR_VOID:
355 p_var->ops = &void_ops;
356 break;
357 default:
358 vlc_assert_unreachable ();
359 }
360
361 if (i_type & VLC_VAR_DOINHERIT)
362 var_Inherit(p_this, psz_name, i_type, &p_var->val);
363
364 vlc_object_internals_t *p_priv = vlc_internals( p_this );
365 variable_t **pp_var, *p_oldvar;
366 int ret = VLC_SUCCESS;
367
368 vlc_mutex_lock( &p_priv->var_lock );
369
370 pp_var = tsearch( p_var, &p_priv->var_root, varcmp );
371 if( unlikely(pp_var == NULL) )
372 ret = VLC_ENOMEM;
373 else if( (p_oldvar = *pp_var) == p_var ) /* Variable create */
374 p_var = NULL; /* Variable created */
375 else /* Variable already exists */
376 {
377 assert (((i_type ^ p_oldvar->i_type) & VLC_VAR_CLASS) == 0);
378 p_oldvar->i_usage++;
379 p_oldvar->i_type |= i_type & VLC_VAR_ISCOMMAND;
380 }
381 vlc_mutex_unlock( &p_priv->var_lock );
382
383 /* If we did not need to create a new variable, free everything... */
384 if( p_var != NULL )
385 Destroy( p_var );
386 return ret;
387 }
388
389 /**
390 * Destroy a vlc variable
391 *
392 * Look for the variable and destroy it if it is found. As in var_Create we
393 * do a call to memmove() but we have performance counterparts elsewhere.
394 *
395 * \param p_this The object that holds the variable
396 * \param psz_name The name of the variable
397 */
398 void (var_Destroy)(vlc_object_t *p_this, const char *psz_name)
399 {
400 variable_t *p_var;
401
402 assert( p_this );
403
404 vlc_object_internals_t *p_priv = vlc_internals( p_this );
405
406 p_var = Lookup( p_this, psz_name );
407 if( p_var == NULL )
408 msg_Dbg( p_this, "attempt to destroy nonexistent variable \"%s\"",
409 psz_name );
410 else if( --p_var->i_usage == 0 )
411 {
412 assert(!p_var->b_incallback);
413 tdelete( p_var, &p_priv->var_root, varcmp );
414 }
415 else
416 {
417 assert(p_var->i_usage != -1u);
418 p_var = NULL;
419 }
420 vlc_mutex_unlock( &p_priv->var_lock );
421
422 if( p_var != NULL )
423 Destroy( p_var );
424 }
425
CleanupVar(void * var)426 static void CleanupVar( void *var )
427 {
428 Destroy( var );
429 }
430
var_DestroyAll(vlc_object_t * obj)431 void var_DestroyAll( vlc_object_t *obj )
432 {
433 vlc_object_internals_t *priv = vlc_internals( obj );
434
435 tdestroy( priv->var_root, CleanupVar );
436 priv->var_root = NULL;
437 }
438
439 #undef var_Change
440 /**
441 * Perform an action on a variable
442 *
443 * \param p_this The object that holds the variable
444 * \param psz_name The name of the variable
445 * \param i_action The action to perform. Must be one of \ref var_action
446 * \param p_val First action parameter
447 * \param p_val2 Second action parameter
448 */
var_Change(vlc_object_t * p_this,const char * psz_name,int i_action,vlc_value_t * p_val,vlc_value_t * p_val2)449 int var_Change( vlc_object_t *p_this, const char *psz_name,
450 int i_action, vlc_value_t *p_val, vlc_value_t *p_val2 )
451 {
452 int ret = VLC_SUCCESS;
453 variable_t *p_var;
454 vlc_value_t oldval;
455 vlc_value_t newval;
456
457 assert( p_this );
458
459 vlc_object_internals_t *p_priv = vlc_internals( p_this );
460
461 p_var = Lookup( p_this, psz_name );
462 if( p_var == NULL )
463 {
464 vlc_mutex_unlock( &p_priv->var_lock );
465 return VLC_ENOVAR;
466 }
467
468 switch( i_action )
469 {
470 case VLC_VAR_GETMIN:
471 *p_val = p_var->min;
472 break;
473 case VLC_VAR_GETMAX:
474 *p_val = p_var->max;
475 break;
476 case VLC_VAR_SETMINMAX:
477 assert(p_var->ops->pf_free == FreeDummy);
478 p_var->min = *p_val;
479 p_var->max = *p_val2;
480 break;
481 case VLC_VAR_SETSTEP:
482 assert(p_var->ops->pf_free == FreeDummy);
483 p_var->step = *p_val;
484 CheckValue( p_var, &p_var->val );
485 break;
486 case VLC_VAR_GETSTEP:
487 switch (p_var->i_type & VLC_VAR_TYPE)
488 {
489 case VLC_VAR_INTEGER:
490 if (p_var->step.i_int == 0)
491 ret = VLC_EGENERIC;
492 break;
493 case VLC_VAR_FLOAT:
494 if (p_var->step.f_float == 0.f)
495 ret = VLC_EGENERIC;
496 break;
497 default:
498 ret = VLC_EGENERIC;
499 }
500 if (ret == VLC_SUCCESS)
501 *p_val = p_var->step;
502 break;
503 case VLC_VAR_ADDCHOICE:
504 {
505 int i = p_var->choices.i_count;
506
507 TAB_APPEND(p_var->choices.i_count,
508 p_var->choices.p_values, *p_val);
509 assert(i == p_var->choices_text.i_count);
510 TAB_APPEND(p_var->choices_text.i_count,
511 p_var->choices_text.p_values, *p_val);
512 p_var->ops->pf_dup( &p_var->choices.p_values[i] );
513 p_var->choices_text.p_values[i].psz_string =
514 ( p_val2 && p_val2->psz_string ) ?
515 strdup( p_val2->psz_string ) : NULL;
516
517 TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_ADDCHOICE, p_val);
518 break;
519 }
520 case VLC_VAR_DELCHOICE:
521 {
522 int i;
523
524 for( i = 0 ; i < p_var->choices.i_count ; i++ )
525 if( p_var->ops->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
526 break;
527
528 if( i == p_var->choices.i_count )
529 {
530 /* Not found */
531 vlc_mutex_unlock( &p_priv->var_lock );
532 return VLC_EGENERIC;
533 }
534
535 p_var->ops->pf_free( &p_var->choices.p_values[i] );
536 free( p_var->choices_text.p_values[i].psz_string );
537 TAB_ERASE(p_var->choices.i_count, p_var->choices.p_values, i);
538 TAB_ERASE(p_var->choices_text.i_count,
539 p_var->choices_text.p_values, i);
540
541 TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_DELCHOICE, p_val);
542 break;
543 }
544 case VLC_VAR_CHOICESCOUNT:
545 p_val->i_int = p_var->choices.i_count;
546 break;
547 case VLC_VAR_CLEARCHOICES:
548 for( int i = 0 ; i < p_var->choices.i_count ; i++ )
549 p_var->ops->pf_free( &p_var->choices.p_values[i] );
550 for( int i = 0 ; i < p_var->choices_text.i_count ; i++ )
551 free( p_var->choices_text.p_values[i].psz_string );
552
553 if( p_var->choices.i_count ) free( p_var->choices.p_values );
554 if( p_var->choices_text.i_count ) free( p_var->choices_text.p_values );
555
556 p_var->choices.i_count = 0;
557 p_var->choices.p_values = NULL;
558 p_var->choices_text.i_count = 0;
559 p_var->choices_text.p_values = NULL;
560 TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_CLEARCHOICES, NULL);
561 break;
562 case VLC_VAR_SETVALUE:
563 /* Duplicate data if needed */
564 newval = *p_val;
565 p_var->ops->pf_dup( &newval );
566 /* Backup needed stuff */
567 oldval = p_var->val;
568 /* Check boundaries and list */
569 CheckValue( p_var, &newval );
570 /* Set the variable */
571 p_var->val = newval;
572 /* Free data if needed */
573 p_var->ops->pf_free( &oldval );
574 break;
575 case VLC_VAR_GETCHOICES:
576 p_val->p_list = xmalloc( sizeof(vlc_list_t) );
577 p_val->p_list->p_values =
578 xmalloc( p_var->choices.i_count * sizeof(vlc_value_t) );
579 p_val->p_list->i_type = p_var->i_type;
580 p_val->p_list->i_count = p_var->choices.i_count;
581 if( p_val2 )
582 {
583 p_val2->p_list = xmalloc( sizeof(vlc_list_t) );
584 p_val2->p_list->p_values =
585 xmalloc( p_var->choices.i_count * sizeof(vlc_value_t) );
586 p_val2->p_list->i_type = VLC_VAR_STRING;
587 p_val2->p_list->i_count = p_var->choices.i_count;
588 }
589 for( int i = 0 ; i < p_var->choices.i_count ; i++ )
590 {
591 p_val->p_list->p_values[i] = p_var->choices.p_values[i];
592 p_var->ops->pf_dup( &p_val->p_list->p_values[i] );
593 if( p_val2 )
594 {
595 p_val2->p_list->p_values[i].psz_string =
596 p_var->choices_text.p_values[i].psz_string ?
597 strdup(p_var->choices_text.p_values[i].psz_string) : NULL;
598 }
599 }
600 break;
601 case VLC_VAR_SETTEXT:
602 free( p_var->psz_text );
603 if( p_val && p_val->psz_string )
604 p_var->psz_text = strdup( p_val->psz_string );
605 else
606 p_var->psz_text = NULL;
607 break;
608 case VLC_VAR_GETTEXT:
609 p_val->psz_string = p_var->psz_text ? strdup( p_var->psz_text )
610 : NULL;
611 break;
612 default:
613 break;
614 }
615
616 vlc_mutex_unlock( &p_priv->var_lock );
617
618 return ret;
619 }
620
621 #undef var_GetAndSet
622 /**
623 * Perform a Get and Set on a variable
624 *
625 * \param p_this: The object that hold the variable
626 * \param psz_name: the name of the variable
627 * \param i_action: the action to perform
628 * \param p_val: The action parameter
629 * \return vlc error codes
630 */
var_GetAndSet(vlc_object_t * p_this,const char * psz_name,int i_action,vlc_value_t * p_val)631 int var_GetAndSet( vlc_object_t *p_this, const char *psz_name, int i_action,
632 vlc_value_t *p_val )
633 {
634 variable_t *p_var;
635 vlc_value_t oldval;
636
637 assert( p_this );
638 assert( p_val );
639
640 vlc_object_internals_t *p_priv = vlc_internals( p_this );
641
642 p_var = Lookup( p_this, psz_name );
643 if( p_var == NULL )
644 {
645 vlc_mutex_unlock( &p_priv->var_lock );
646 return VLC_ENOVAR;
647 }
648
649 WaitUnused( p_this, p_var );
650
651 /* Duplicated data if needed */
652 //p_var->ops->pf_dup( &val );
653
654 /* Backup needed stuff */
655 oldval = p_var->val;
656
657 /* depending of the action requiered */
658 switch( i_action )
659 {
660 case VLC_VAR_BOOL_TOGGLE:
661 assert( ( p_var->i_type & VLC_VAR_BOOL ) == VLC_VAR_BOOL );
662 p_var->val.b_bool = !p_var->val.b_bool;
663 break;
664 case VLC_VAR_INTEGER_ADD:
665 assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
666 p_var->val.i_int += p_val->i_int;
667 break;
668 case VLC_VAR_INTEGER_OR:
669 assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
670 p_var->val.i_int |= p_val->i_int;
671 break;
672 case VLC_VAR_INTEGER_NAND:
673 assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
674 p_var->val.i_int &= ~p_val->i_int;
675 break;
676 default:
677 vlc_mutex_unlock( &p_priv->var_lock );
678 return VLC_EGENERIC;
679 }
680
681 /* Check boundaries */
682 CheckValue( p_var, &p_var->val );
683 *p_val = p_var->val;
684
685 /* Deal with callbacks.*/
686 TriggerCallback( p_this, p_var, psz_name, oldval );
687
688 vlc_mutex_unlock( &p_priv->var_lock );
689 return VLC_SUCCESS;
690 }
691
692 #undef var_Type
693 /**
694 * Request a variable's type
695 *
696 * \return The variable type if it exists, or 0 if the
697 * variable could not be found.
698 * \see \ref var_type
699 */
var_Type(vlc_object_t * p_this,const char * psz_name)700 int var_Type( vlc_object_t *p_this, const char *psz_name )
701 {
702 variable_t *p_var;
703 int i_type = 0;
704
705 assert( p_this );
706
707 vlc_object_internals_t *p_priv = vlc_internals( p_this );
708
709 p_var = Lookup( p_this, psz_name );
710 if( p_var != NULL )
711 {
712 i_type = p_var->i_type;
713 if( p_var->choices.i_count > 0 )
714 i_type |= VLC_VAR_HASCHOICE;
715 }
716 vlc_mutex_unlock( &p_priv->var_lock );
717
718 return i_type;
719 }
720
721 #undef var_SetChecked
var_SetChecked(vlc_object_t * p_this,const char * psz_name,int expected_type,vlc_value_t val)722 int var_SetChecked( vlc_object_t *p_this, const char *psz_name,
723 int expected_type, vlc_value_t val )
724 {
725 variable_t *p_var;
726 vlc_value_t oldval;
727
728 assert( p_this );
729
730 vlc_object_internals_t *p_priv = vlc_internals( p_this );
731
732 p_var = Lookup( p_this, psz_name );
733 if( p_var == NULL )
734 {
735 vlc_mutex_unlock( &p_priv->var_lock );
736 return VLC_ENOVAR;
737 }
738
739 assert( expected_type == 0 ||
740 (p_var->i_type & VLC_VAR_CLASS) == expected_type );
741 assert ((p_var->i_type & VLC_VAR_CLASS) != VLC_VAR_VOID);
742
743 WaitUnused( p_this, p_var );
744
745 /* Duplicate data if needed */
746 p_var->ops->pf_dup( &val );
747
748 /* Backup needed stuff */
749 oldval = p_var->val;
750
751 /* Check boundaries and list */
752 CheckValue( p_var, &val );
753
754 /* Set the variable */
755 p_var->val = val;
756
757 /* Deal with callbacks */
758 TriggerCallback( p_this, p_var, psz_name, oldval );
759
760 /* Free data if needed */
761 p_var->ops->pf_free( &oldval );
762
763 vlc_mutex_unlock( &p_priv->var_lock );
764 return VLC_SUCCESS;
765 }
766
767 #undef var_Set
768 /**
769 * Set a variable's value
770 *
771 * \param p_this The object that hold the variable
772 * \param psz_name The name of the variable
773 * \param val the value to set
774 */
var_Set(vlc_object_t * p_this,const char * psz_name,vlc_value_t val)775 int var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
776 {
777 return var_SetChecked( p_this, psz_name, 0, val );
778 }
779
780 #undef var_GetChecked
var_GetChecked(vlc_object_t * p_this,const char * psz_name,int expected_type,vlc_value_t * p_val)781 int var_GetChecked( vlc_object_t *p_this, const char *psz_name,
782 int expected_type, vlc_value_t *p_val )
783 {
784 assert( p_this );
785
786 vlc_object_internals_t *p_priv = vlc_internals( p_this );
787 variable_t *p_var;
788 int err = VLC_SUCCESS;
789
790 p_var = Lookup( p_this, psz_name );
791 if( p_var != NULL )
792 {
793 assert( expected_type == 0 ||
794 (p_var->i_type & VLC_VAR_CLASS) == expected_type );
795 assert ((p_var->i_type & VLC_VAR_CLASS) != VLC_VAR_VOID);
796
797 /* Really get the variable */
798 *p_val = p_var->val;
799
800 /* Duplicate value if needed */
801 p_var->ops->pf_dup( p_val );
802 }
803 else
804 err = VLC_ENOVAR;
805
806 vlc_mutex_unlock( &p_priv->var_lock );
807 return err;
808 }
809
810 #undef var_Get
811 /**
812 * Get a variable's value
813 *
814 * \param p_this The object that holds the variable
815 * \param psz_name The name of the variable
816 * \param p_val Pointer to a vlc_value_t that will hold the variable's value
817 * after the function is finished
818 */
var_Get(vlc_object_t * p_this,const char * psz_name,vlc_value_t * p_val)819 int var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
820 {
821 return var_GetChecked( p_this, psz_name, 0, p_val );
822 }
823
824 typedef enum
825 {
826 vlc_value_callback,
827 vlc_list_callback
828 } vlc_callback_type_t;
829
AddCallback(vlc_object_t * p_this,const char * psz_name,callback_entry_t entry,vlc_callback_type_t i_type)830 static void AddCallback( vlc_object_t *p_this, const char *psz_name,
831 callback_entry_t entry, vlc_callback_type_t i_type )
832 {
833 variable_t *p_var;
834
835 assert( p_this );
836
837 vlc_object_internals_t *p_priv = vlc_internals( p_this );
838
839 p_var = Lookup( p_this, psz_name );
840 if( p_var == NULL )
841 {
842 vlc_mutex_unlock( &p_priv->var_lock );
843 msg_Err( p_this, "cannot add callback %p to nonexistent variable '%s'",
844 entry.p_callback, psz_name );
845 return;
846 }
847
848 WaitUnused( p_this, p_var );
849
850 callback_table_t *p_table;
851 if (i_type == vlc_value_callback)
852 p_table = &p_var->value_callbacks;
853 else
854 p_table = &p_var->list_callbacks;
855 TAB_APPEND(p_table->i_entries, p_table->p_entries, entry);
856
857 vlc_mutex_unlock( &p_priv->var_lock );
858 }
859
860 #undef var_AddCallback
861 /**
862 * Register a callback in a variable
863 *
864 * We store a function pointer that will be called upon variable
865 * modification.
866 *
867 * \param p_this The object that holds the variable
868 * \param psz_name The name of the variable
869 * \param pf_callback The function pointer
870 * \param p_data A generic pointer that will be passed as the last
871 * argument to the callback function.
872 *
873 * \warning The callback function is run in the thread that calls var_Set on
874 * the variable. Use proper locking. This thread may not have much
875 * time to spare, so keep callback functions short.
876 */
var_AddCallback(vlc_object_t * p_this,const char * psz_name,vlc_callback_t pf_callback,void * p_data)877 void var_AddCallback( vlc_object_t *p_this, const char *psz_name,
878 vlc_callback_t pf_callback, void *p_data )
879 {
880 callback_entry_t entry;
881 entry.pf_value_callback = pf_callback;
882 entry.p_data = p_data;
883
884 AddCallback(p_this, psz_name, entry, vlc_value_callback);
885 }
886
DelCallback(vlc_object_t * p_this,const char * psz_name,callback_entry_t entry,vlc_callback_type_t i_type)887 static void DelCallback( vlc_object_t *p_this, const char *psz_name,
888 callback_entry_t entry, vlc_callback_type_t i_type )
889 {
890 int i_entry;
891 variable_t *p_var;
892 #ifndef NDEBUG
893 bool b_found_similar = false;
894 #endif
895
896 assert( p_this );
897
898 vlc_object_internals_t *p_priv = vlc_internals( p_this );
899
900 p_var = Lookup( p_this, psz_name );
901 if( p_var == NULL )
902 {
903 vlc_mutex_unlock( &p_priv->var_lock );
904 msg_Err( p_this, "cannot delete callback %p from nonexistent "
905 "variable '%s'", entry.p_callback, psz_name );
906 return;
907 }
908
909 WaitUnused( p_this, p_var );
910
911 callback_table_t *p_table;
912 if (i_type == vlc_value_callback)
913 p_table = &p_var->value_callbacks;
914 else
915 p_table = &p_var->list_callbacks;
916
917 for( i_entry = p_table->i_entries ; i_entry-- ; )
918 {
919 if( p_table->p_entries[i_entry].p_callback == entry.p_callback
920 && p_table->p_entries[i_entry].p_data == entry.p_data )
921 {
922 break;
923 }
924 #ifndef NDEBUG
925 else if( p_table->p_entries[i_entry].p_callback == entry.p_callback )
926 b_found_similar = true;
927 #endif
928 }
929
930 if( i_entry < 0 )
931 {
932 #ifndef NDEBUG
933 if( b_found_similar )
934 fprintf( stderr, "Calling var_DelCallback for '%s' with the same "
935 "function but not the same data.", psz_name );
936 vlc_assert_unreachable();
937 #endif
938 vlc_mutex_unlock( &p_priv->var_lock );
939 return;
940 }
941
942 TAB_ERASE(p_table->i_entries, p_table->p_entries, i_entry);
943
944 vlc_mutex_unlock( &p_priv->var_lock );
945 }
946
947 #undef var_DelCallback
948 /**
949 * Remove a callback from a variable
950 *
951 * pf_callback and p_data have to be given again, because different objects
952 * might have registered the same callback function.
953 */
var_DelCallback(vlc_object_t * p_this,const char * psz_name,vlc_callback_t pf_callback,void * p_data)954 void var_DelCallback( vlc_object_t *p_this, const char *psz_name,
955 vlc_callback_t pf_callback, void *p_data )
956 {
957 callback_entry_t entry;
958 entry.pf_value_callback = pf_callback;
959 entry.p_data = p_data;
960
961 DelCallback(p_this, psz_name, entry, vlc_value_callback);
962 }
963
964 #undef var_TriggerCallback
965 /**
966 * Trigger callback on a variable
967 *
968 * \param p_this The object that hold the variable
969 * \param psz_name The name of the variable
970 */
var_TriggerCallback(vlc_object_t * p_this,const char * psz_name)971 void var_TriggerCallback( vlc_object_t *p_this, const char *psz_name )
972 {
973 vlc_object_internals_t *p_priv = vlc_internals( p_this );
974 variable_t *p_var = Lookup( p_this, psz_name );
975 if( p_var != NULL )
976 {
977 WaitUnused( p_this, p_var );
978
979 /* Deal with callbacks. Tell we're in a callback, release the lock,
980 * call stored functions, retake the lock. */
981 TriggerCallback( p_this, p_var, psz_name, p_var->val );
982 }
983 vlc_mutex_unlock( &p_priv->var_lock );
984 }
985
986 #undef var_AddListCallback
987 /**
988 * Register a callback for a list variable
989 *
990 * The callback is triggered when an element is added/removed from the
991 * list or when the list is cleared.
992 *
993 * See var_AddCallback().
994 */
var_AddListCallback(vlc_object_t * p_this,const char * psz_name,vlc_list_callback_t pf_callback,void * p_data)995 void var_AddListCallback( vlc_object_t *p_this, const char *psz_name,
996 vlc_list_callback_t pf_callback, void *p_data )
997 {
998 callback_entry_t entry;
999 entry.pf_list_callback = pf_callback;
1000 entry.p_data = p_data;
1001
1002 AddCallback(p_this, psz_name, entry, vlc_list_callback);
1003 }
1004
1005 #undef var_DelListCallback
1006 /**
1007 * Remove a callback from a list variable
1008 *
1009 * See var_DelCallback().
1010 */
var_DelListCallback(vlc_object_t * p_this,const char * psz_name,vlc_list_callback_t pf_callback,void * p_data)1011 void var_DelListCallback( vlc_object_t *p_this, const char *psz_name,
1012 vlc_list_callback_t pf_callback, void *p_data )
1013 {
1014 callback_entry_t entry;
1015 entry.pf_list_callback = pf_callback;
1016 entry.p_data = p_data;
1017
1018 DelCallback(p_this, psz_name, entry, vlc_list_callback);
1019 }
1020
1021 /** Parse a stringified option
1022 * This function parse a string option and create the associated object
1023 * variable
1024 * The option must be of the form "[no[-]]foo[=bar]" where foo is the
1025 * option name and bar is the value of the option.
1026 * \param p_obj the object in which the variable must be created
1027 * \param psz_option the option to parse
1028 * \param trusted whether the option is set by a trusted input or not
1029 * \return nothing
1030 */
var_OptionParse(vlc_object_t * p_obj,const char * psz_option,bool trusted)1031 void var_OptionParse( vlc_object_t *p_obj, const char *psz_option,
1032 bool trusted )
1033 {
1034 char *psz_name, *psz_value;
1035 int i_type;
1036 bool b_isno = false;
1037 vlc_value_t val;
1038
1039 val.psz_string = NULL;
1040
1041 /* It's too much of a hassle to remove the ':' when we parse
1042 * the cmd line :) */
1043 if( psz_option[0] == ':' )
1044 psz_option++;
1045
1046 if( !psz_option[0] )
1047 return;
1048
1049 psz_name = strdup( psz_option );
1050 if( psz_name == NULL )
1051 return;
1052
1053 psz_value = strchr( psz_name, '=' );
1054 if( psz_value != NULL )
1055 *psz_value++ = '\0';
1056
1057 i_type = config_GetType( psz_name );
1058 if( !i_type && !psz_value )
1059 {
1060 /* check for "no-foo" or "nofoo" */
1061 if( !strncmp( psz_name, "no-", 3 ) )
1062 {
1063 memmove( psz_name, psz_name + 3, strlen(psz_name) + 1 - 3 );
1064 }
1065 else if( !strncmp( psz_name, "no", 2 ) )
1066 {
1067 memmove( psz_name, psz_name + 2, strlen(psz_name) + 1 - 2 );
1068 }
1069 else goto cleanup; /* Option doesn't exist */
1070
1071 b_isno = true;
1072 i_type = config_GetType( psz_name );
1073 }
1074 if( !i_type ) goto cleanup; /* Option doesn't exist */
1075
1076 if( ( i_type != VLC_VAR_BOOL ) &&
1077 ( !psz_value || !*psz_value ) ) goto cleanup; /* Invalid value */
1078
1079 /* check if option is unsafe */
1080 if( !trusted && !config_IsSafe( psz_name ) )
1081 {
1082 msg_Err( p_obj, "unsafe option \"%s\" has been ignored for "
1083 "security reasons", psz_name );
1084 free( psz_name );
1085 return;
1086 }
1087
1088 /* Create the variable in the input object.
1089 * Children of the input object will be able to retrieve this value
1090 * thanks to the inheritance property of the object variables. */
1091 var_Create( p_obj, psz_name, i_type );
1092
1093 switch( i_type )
1094 {
1095 case VLC_VAR_BOOL:
1096 val.b_bool = !b_isno;
1097 break;
1098
1099 case VLC_VAR_INTEGER:
1100 val.i_int = strtoll( psz_value, NULL, 0 );
1101 break;
1102
1103 case VLC_VAR_FLOAT:
1104 val.f_float = us_atof( psz_value );
1105 break;
1106
1107 case VLC_VAR_STRING:
1108 val.psz_string = psz_value;
1109 break;
1110
1111 default:
1112 goto cleanup;
1113 }
1114
1115 var_Set( p_obj, psz_name, val );
1116
1117 cleanup:
1118 free( psz_name );
1119 }
1120
1121 #undef var_LocationParse
1122 /**
1123 * Parses a set of colon-separated or semicolon-separated
1124 * <code>name=value</code> pairs.
1125 * Some access (or access_demux) plugins uses this scheme
1126 * in media resource location.
1127 * @note Only trusted/safe variables are allowed. This is intended.
1128 *
1129 * @warning Only use this for plugins implementing VLC-specific resource
1130 * location schemes. This would not make any sense for standardized ones.
1131 *
1132 * @param obj VLC object on which to set variables (and emit error messages)
1133 * @param mrl string to parse
1134 * @param pref prefix to prepend to option names in the string
1135 *
1136 * @return VLC_ENOMEM on error, VLC_SUCCESS on success.
1137 */
var_LocationParse(vlc_object_t * obj,const char * mrl,const char * pref)1138 int var_LocationParse (vlc_object_t *obj, const char *mrl, const char *pref)
1139 {
1140 int ret = VLC_SUCCESS;
1141 size_t preflen = strlen (pref) + 1;
1142
1143 assert(mrl != NULL);
1144 while (*mrl != '\0')
1145 {
1146 mrl += strspn (mrl, ":;"); /* skip leading colon(s) */
1147
1148 size_t len = strcspn (mrl, ":;");
1149 char *buf = malloc (preflen + len);
1150
1151 if (likely(buf != NULL))
1152 {
1153 /* NOTE: this does not support the "no-<varname>" bool syntax. */
1154 /* DO NOT use asprintf() here; it won't work! Think again. */
1155 snprintf (buf, preflen + len, "%s%s", pref, mrl);
1156 var_OptionParse (obj, buf, false);
1157 free (buf);
1158 }
1159 else
1160 ret = VLC_ENOMEM;
1161 mrl += len;
1162 }
1163
1164 return ret;
1165 }
1166
1167 /**
1168 * Finds the value of a variable. If the specified object does not hold a
1169 * variable with the specified name, try the parent object, and iterate until
1170 * the top of the tree. If no match is found, the value is read from the
1171 * configuration.
1172 */
var_Inherit(vlc_object_t * p_this,const char * psz_name,int i_type,vlc_value_t * p_val)1173 int var_Inherit( vlc_object_t *p_this, const char *psz_name, int i_type,
1174 vlc_value_t *p_val )
1175 {
1176 i_type &= VLC_VAR_CLASS;
1177 for( vlc_object_t *obj = p_this; obj != NULL; obj = obj->obj.parent )
1178 {
1179 if( var_GetChecked( obj, psz_name, i_type, p_val ) == VLC_SUCCESS )
1180 return VLC_SUCCESS;
1181 }
1182
1183 /* else take value from config */
1184 switch( i_type & VLC_VAR_CLASS )
1185 {
1186 case VLC_VAR_STRING:
1187 p_val->psz_string = config_GetPsz( p_this, psz_name );
1188 if( !p_val->psz_string ) p_val->psz_string = strdup("");
1189 break;
1190 case VLC_VAR_FLOAT:
1191 p_val->f_float = config_GetFloat( p_this, psz_name );
1192 break;
1193 case VLC_VAR_INTEGER:
1194 p_val->i_int = config_GetInt( p_this, psz_name );
1195 break;
1196 case VLC_VAR_BOOL:
1197 p_val->b_bool = config_GetInt( p_this, psz_name ) > 0;
1198 break;
1199 default:
1200 vlc_assert_unreachable();
1201 case VLC_VAR_ADDRESS:
1202 return VLC_ENOOBJ;
1203 }
1204 return VLC_SUCCESS;
1205 }
1206
1207
1208 /**
1209 * It inherits a string as an unsigned rational number (it also accepts basic
1210 * float number).
1211 *
1212 * It returns an error if the rational number cannot be parsed (0/0 is valid).
1213 * The rational is already reduced.
1214 */
1215 int (var_InheritURational)(vlc_object_t *object,
1216 unsigned *num, unsigned *den,
1217 const char *var)
1218 {
1219 char *str = var_InheritString(object, var);
1220 if (str == NULL)
1221 goto error;
1222
1223 char *sep;
1224 unsigned n = strtoul(str, &sep, 10);
1225 unsigned d;
1226
1227 switch (*sep) {
1228 case '\0':
1229 /* Decimal integer */
1230 d = 1;
1231 break;
1232
1233 case ':':
1234 case '/':
1235 /* Decimal fraction */
1236 d = strtoul(sep + 1, &sep, 10);
1237 if (*sep != '\0')
1238 goto error;
1239 break;
1240
1241 case '.': {
1242 /* Decimal number */
1243 unsigned char c;
1244
1245 d = 1;
1246 while ((c = *(++sep)) != '\0') {
1247 c -= '0';
1248
1249 if (c >= 10)
1250 goto error;
1251
1252 n = n * 10 + c;
1253 d *= 10;
1254 }
1255 break;
1256 }
1257
1258 default:
1259 goto error;
1260 }
1261
1262 free(str);
1263
1264 if (n == 0) {
1265 *num = 0;
1266 *den = d ? 1 : 0;
1267 } else if (d == 0) {
1268 *num = 1;
1269 *den = 0;
1270 } else
1271 vlc_ureduce(num, den, n, d, 0);
1272
1273 return VLC_SUCCESS;
1274
1275 error:
1276 free(str);
1277 *num = 0;
1278 *den = 0;
1279 return VLC_EGENERIC;
1280 }
1281
1282 /**
1283 * Free a list and the associated strings
1284 * @param p_val: the list variable
1285 * @param p_val2: the variable associated or NULL
1286 */
var_FreeList(vlc_value_t * p_val,vlc_value_t * p_val2)1287 void var_FreeList( vlc_value_t *p_val, vlc_value_t *p_val2 )
1288 {
1289 switch( p_val->p_list->i_type & VLC_VAR_CLASS )
1290 {
1291 case VLC_VAR_STRING:
1292 for( int i = 0; i < p_val->p_list->i_count; i++ )
1293 free( p_val->p_list->p_values[i].psz_string );
1294 break;
1295 }
1296
1297 free( p_val->p_list->p_values );
1298 free( p_val->p_list );
1299
1300 if( p_val2 != NULL )
1301 {
1302 assert( p_val2->p_list != NULL );
1303 assert( p_val2->p_list->i_type == VLC_VAR_STRING );
1304
1305 for( int i = 0; i < p_val2->p_list->i_count; i++ )
1306 free( p_val2->p_list->p_values[i].psz_string );
1307 free( p_val2->p_list->p_values );
1308 free( p_val2->p_list );
1309 }
1310 }
1311
DumpVariable(const void * data,const VISIT which,const int depth)1312 static void DumpVariable(const void *data, const VISIT which, const int depth)
1313 {
1314 if (which != postorder && which != leaf)
1315 return;
1316 (void) depth;
1317
1318 const variable_t *var = *(const variable_t **)data;
1319 const char *typename = "unknown";
1320
1321 switch (var->i_type & VLC_VAR_TYPE)
1322 {
1323 case VLC_VAR_VOID: typename = "void"; break;
1324 case VLC_VAR_BOOL: typename = "bool"; break;
1325 case VLC_VAR_INTEGER: typename = "integer"; break;
1326 case VLC_VAR_STRING: typename = "string"; break;
1327 case VLC_VAR_FLOAT: typename = "float"; break;
1328 case VLC_VAR_COORDS: typename = "coordinates"; break;
1329 case VLC_VAR_ADDRESS: typename = "address"; break;
1330 default: typename = "unknown"; break;
1331 }
1332
1333 printf(" *-o \"%s\" (%s", var->psz_name, typename);
1334 if (var->psz_text != NULL)
1335 printf(", %s", var->psz_text);
1336 putchar(')');
1337 if (var->i_type & VLC_VAR_HASCHOICE)
1338 fputs(", has choices", stdout);
1339 if (var->i_type & VLC_VAR_ISCOMMAND)
1340 fputs(", command", stdout);
1341 if (var->value_callbacks.i_entries)
1342 printf(", %d callbacks", var->value_callbacks.i_entries);
1343
1344 switch (var->i_type & VLC_VAR_CLASS)
1345 {
1346 case VLC_VAR_VOID:
1347 break;
1348 case VLC_VAR_BOOL:
1349 printf(": %s", var->val.b_bool ? "true" : "false");
1350 break;
1351 case VLC_VAR_INTEGER:
1352 printf(": %"PRId64, var->val.i_int );
1353 break;
1354 case VLC_VAR_STRING:
1355 printf(": \"%s\"", var->val.psz_string );
1356 break;
1357 case VLC_VAR_FLOAT:
1358 printf(": %f", var->val.f_float );
1359 break;
1360 case VLC_VAR_COORDS:
1361 printf(": %"PRId32"x%"PRId32,
1362 var->val.coords.x, var->val.coords.y);
1363 break;
1364 case VLC_VAR_ADDRESS:
1365 printf(": %p", var->val.p_address);
1366 break;
1367 }
1368 putchar('\n');
1369 }
1370
DumpVariables(vlc_object_t * obj)1371 void DumpVariables(vlc_object_t *obj)
1372 {
1373 vlc_mutex_lock(&vlc_internals(obj)->var_lock);
1374 if (vlc_internals(obj)->var_root == NULL)
1375 puts(" `-o No variables");
1376 else
1377 twalk(vlc_internals(obj)->var_root, DumpVariable);
1378 vlc_mutex_unlock(&vlc_internals(obj)->var_lock);
1379 }
1380
1381 static thread_local void *twalk_ctx;
1382
TwalkGetNames(const void * data,const VISIT which,const int depth)1383 static void TwalkGetNames(const void *data, const VISIT which, const int depth)
1384 {
1385 if (which != postorder && which != leaf)
1386 return;
1387 (void) depth;
1388
1389 const variable_t *var = *(const variable_t **)data;
1390 DECL_ARRAY(char *) *names = twalk_ctx;
1391 char *dup = strdup(var->psz_name);
1392 if (dup != NULL)
1393 ARRAY_APPEND(*names, dup);
1394 }
1395
var_GetAllNames(vlc_object_t * obj)1396 char **var_GetAllNames(vlc_object_t *obj)
1397 {
1398 vlc_object_internals_t *priv = vlc_internals(obj);
1399
1400 DECL_ARRAY(char *) names;
1401 ARRAY_INIT(names);
1402
1403 twalk_ctx = &names;
1404 vlc_mutex_lock(&priv->var_lock);
1405 twalk(priv->var_root, TwalkGetNames);
1406 vlc_mutex_unlock(&priv->var_lock);
1407
1408 if (names.i_size == 0)
1409 return NULL;
1410 ARRAY_APPEND(names, NULL);
1411 return names.p_elems;
1412 }
1413