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