]> git.sesse.net Git - vlc/blob - src/misc/variables.c
Hide internal variables state
[vlc] / src / misc / variables.c
1 /*****************************************************************************
2  * variables.c: routines for object variables handling
3  *****************************************************************************
4  * Copyright (C) 2002-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include "variables.h"
29
30 #ifdef HAVE_STDLIB_H
31 #   include <stdlib.h>                                          /* realloc() */
32 #endif
33 #include "libvlc.h"
34
35 /*****************************************************************************
36  * Private types
37  *****************************************************************************/
38 struct callback_entry_t
39 {
40     vlc_callback_t pf_callback;
41     void *         p_data;
42 };
43
44 /*****************************************************************************
45  * Local comparison functions, returns 0 if v == w, < 0 if v < w, > 0 if v > w
46  *****************************************************************************/
47 static int CmpBool( vlc_value_t v, vlc_value_t w ) { return v.b_bool ? w.b_bool ? 0 : 1 : w.b_bool ? -1 : 0; }
48 static int CmpInt( vlc_value_t v, vlc_value_t w ) { return v.i_int == w.i_int ? 0 : v.i_int > w.i_int ? 1 : -1; }
49 static int CmpTime( vlc_value_t v, vlc_value_t w )
50 {
51     return v.i_time == w.i_time ? 0 : v.i_time > w.i_time ? 1 : -1;
52 }
53 static int CmpString( vlc_value_t v, vlc_value_t w ) { return strcmp( v.psz_string, w.psz_string ); }
54 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; }
55 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; }
56
57 /*****************************************************************************
58  * Local duplication functions, and local deallocation functions
59  *****************************************************************************/
60 static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
61 static void DupString( vlc_value_t *p_val ) { p_val->psz_string = strdup( p_val->psz_string ); }
62
63 static void DupList( vlc_value_t *p_val )
64 {
65     int i;
66     vlc_list_t *p_list = malloc( sizeof(vlc_list_t) );
67
68     p_list->i_count = p_val->p_list->i_count;
69     if( p_val->p_list->i_count )
70     {
71         p_list->p_values = malloc( p_list->i_count * sizeof(vlc_value_t) );
72         p_list->pi_types = malloc( p_list->i_count * sizeof(int) );
73     }
74     else
75     {
76         p_list->p_values = NULL;
77         p_list->pi_types = NULL;
78     }
79
80     for( i = 0; i < p_list->i_count; i++ )
81     {
82         p_list->p_values[i] = p_val->p_list->p_values[i];
83         p_list->pi_types[i] = p_val->p_list->pi_types[i];
84         switch( p_val->p_list->pi_types[i] & VLC_VAR_TYPE )
85         {
86         case VLC_VAR_STRING:
87
88             DupString( &p_list->p_values[i] );
89             break;
90         default:
91             break;
92         }
93     }
94
95     p_val->p_list = p_list;
96 }
97
98 static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
99 static void FreeString( vlc_value_t *p_val ) { free( p_val->psz_string ); }
100 static void FreeMutex( vlc_value_t *p_val ) { vlc_mutex_destroy( (vlc_mutex_t*)p_val->p_address ); free( p_val->p_address ); }
101
102 static void FreeList( vlc_value_t *p_val )
103 {
104     int i;
105     for( i = 0; i < p_val->p_list->i_count; i++ )
106     {
107         switch( p_val->p_list->pi_types[i] & VLC_VAR_TYPE )
108         {
109         case VLC_VAR_STRING:
110             FreeString( &p_val->p_list->p_values[i] );
111             break;
112         case VLC_VAR_MUTEX:
113             FreeMutex( &p_val->p_list->p_values[i] );
114             break;
115         default:
116             break;
117         }
118     }
119
120     if( p_val->p_list->i_count )
121     {
122         free( p_val->p_list->p_values );
123         free( p_val->p_list->pi_types );
124     }
125     free( p_val->p_list );
126 }
127
128 /*****************************************************************************
129  * Local prototypes
130  *****************************************************************************/
131 static int      GetUnused   ( vlc_object_t *, const char * );
132 static uint32_t HashString  ( const char * );
133 static int      Insert      ( variable_t *, int, const char * );
134 static int      InsertInner ( variable_t *, int, uint32_t );
135 static int      Lookup      ( variable_t *, int, const char * );
136 static int      LookupInner ( variable_t *, int, uint32_t );
137
138 static void     CheckValue  ( variable_t *, vlc_value_t * );
139
140 static int      InheritValue( vlc_object_t *, const char *, vlc_value_t *,
141                               int );
142
143 /**
144  * Initialize a vlc variable
145  *
146  * We hash the given string and insert it into the sorted list. The insertion
147  * may require slow memory copies, but think about what we gain in the log(n)
148  * lookup phase when setting/getting the variable value!
149  *
150  * \param p_this The object in which to create the variable
151  * \param psz_name The name of the variable
152  * \param i_type The variables type. Must be one of \ref var_type combined with
153  *               zero or more \ref var_flags
154  */
155 int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
156 {
157     int i_new;
158     variable_t *p_var;
159     static vlc_list_t dummy_null_list = {0, NULL, NULL};
160     vlc_object_internals_t *p_priv = p_this->p_internals;
161
162     vlc_mutex_lock( &p_priv->var_lock );
163
164     /* FIXME: if the variable already exists, we don't duplicate it. But we
165      * duplicate the lookups. It's not that serious, but if anyone finds some
166      * time to rework Insert() so that only one lookup has to be done, feel
167      * free to do so. */
168     i_new = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
169
170     if( i_new >= 0 )
171     {
172         /* If the types differ, variable creation failed. */
173         if( (i_type & ~(VLC_VAR_DOINHERIT|VLC_VAR_ISCOMMAND)) != p_priv->p_vars[i_new].i_type )
174         {
175             vlc_mutex_unlock( &p_priv->var_lock );
176             return VLC_EBADVAR;
177         }
178
179         p_priv->p_vars[i_new].i_usage++;
180         if( i_type & VLC_VAR_ISCOMMAND )
181             p_priv->p_vars[i_new].i_type |= VLC_VAR_ISCOMMAND;
182         vlc_mutex_unlock( &p_priv->var_lock );
183         return VLC_SUCCESS;
184     }
185
186     i_new = Insert( p_priv->p_vars, p_priv->i_vars, psz_name );
187
188     if( (p_priv->i_vars & 15) == 15 )
189     {
190         p_priv->p_vars = realloc( p_priv->p_vars,
191                                   (p_priv->i_vars+17) * sizeof(variable_t) );
192     }
193
194     memmove( p_priv->p_vars + i_new + 1,
195              p_priv->p_vars + i_new,
196              (p_priv->i_vars - i_new) * sizeof(variable_t) );
197
198     p_priv->i_vars++;
199
200     p_var = &p_priv->p_vars[i_new];
201     memset( p_var, 0, sizeof(*p_var) );
202
203     p_var->i_hash = HashString( psz_name );
204     p_var->psz_name = strdup( psz_name );
205     p_var->psz_text = NULL;
206
207     p_var->i_type = i_type & ~VLC_VAR_DOINHERIT;
208     memset( &p_var->val, 0, sizeof(vlc_value_t) );
209
210     p_var->pf_dup = DupDummy;
211     p_var->pf_free = FreeDummy;
212
213     p_var->i_usage = 1;
214
215     p_var->i_default = -1;
216     p_var->choices.i_count = 0;
217     p_var->choices.p_values = NULL;
218     p_var->choices_text.i_count = 0;
219     p_var->choices_text.p_values = NULL;
220
221     p_var->b_incallback = VLC_FALSE;
222     p_var->i_entries = 0;
223     p_var->p_entries = NULL;
224
225     /* Always initialize the variable, even if it is a list variable; this
226      * will lead to errors if the variable is not initialized, but it will
227      * not cause crashes in the variable handling. */
228     switch( i_type & VLC_VAR_TYPE )
229     {
230         case VLC_VAR_BOOL:
231             p_var->pf_cmp = CmpBool;
232             p_var->val.b_bool = VLC_FALSE;
233             break;
234         case VLC_VAR_INTEGER:
235         case VLC_VAR_HOTKEY:
236             p_var->pf_cmp = CmpInt;
237             p_var->val.i_int = 0;
238             break;
239         case VLC_VAR_STRING:
240         case VLC_VAR_MODULE:
241         case VLC_VAR_FILE:
242         case VLC_VAR_DIRECTORY:
243         case VLC_VAR_VARIABLE:
244             p_var->pf_cmp = CmpString;
245             p_var->pf_dup = DupString;
246             p_var->pf_free = FreeString;
247             p_var->val.psz_string = "";
248             break;
249         case VLC_VAR_FLOAT:
250             p_var->pf_cmp = CmpFloat;
251             p_var->val.f_float = 0.0;
252             break;
253         case VLC_VAR_TIME:
254             p_var->pf_cmp = CmpTime;
255             p_var->val.i_time = 0;
256             break;
257         case VLC_VAR_ADDRESS:
258             p_var->pf_cmp = CmpAddress;
259             p_var->val.p_address = NULL;
260             break;
261         case VLC_VAR_MUTEX:
262             p_var->pf_cmp = CmpAddress;
263             p_var->pf_free = FreeMutex;
264             p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
265             vlc_mutex_init( p_this, (vlc_mutex_t*)p_var->val.p_address );
266             break;
267         case VLC_VAR_LIST:
268             p_var->pf_cmp = CmpAddress;
269             p_var->pf_dup = DupList;
270             p_var->pf_free = FreeList;
271             p_var->val.p_list = &dummy_null_list;
272             break;
273     }
274
275     /* Duplicate the default data we stored. */
276     p_var->pf_dup( &p_var->val );
277
278     if( i_type & VLC_VAR_DOINHERIT )
279     {
280         vlc_value_t val;
281
282         if( InheritValue( p_this, psz_name, &val, p_var->i_type )
283             == VLC_SUCCESS )
284         {
285             /* Free data if needed */
286             p_var->pf_free( &p_var->val );
287             /* Set the variable */
288             p_var->val = val;
289
290             if( i_type & VLC_VAR_HASCHOICE )
291             {
292                 /* We must add the inherited value to our choice list */
293                 p_var->i_default = 0;
294
295                 INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
296                              0, val );
297                 INSERT_ELEM( p_var->choices_text.p_values,
298                              p_var->choices_text.i_count, 0, val );
299                 p_var->pf_dup( &p_var->choices.p_values[0] );
300                 p_var->choices_text.p_values[0].psz_string = NULL;
301             }
302         }
303     }
304
305     vlc_mutex_unlock( &p_priv->var_lock );
306
307     return VLC_SUCCESS;
308 }
309
310 /**
311  * Destroy a vlc variable
312  *
313  * Look for the variable and destroy it if it is found. As in var_Create we
314  * do a call to memmove() but we have performance counterparts elsewhere.
315  *
316  * \param p_this The object that holds the variable
317  * \param psz_name The name of the variable
318  */
319 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
320 {
321     int i_var, i;
322     variable_t *p_var;
323     vlc_object_internals_t *p_priv = p_this->p_internals;
324
325     vlc_mutex_lock( &p_priv->var_lock );
326
327     i_var = GetUnused( p_this, psz_name );
328     if( i_var < 0 )
329     {
330         vlc_mutex_unlock( &p_priv->var_lock );
331         return i_var;
332     }
333
334     p_var = &p_priv->p_vars[i_var];
335
336     if( p_var->i_usage > 1 )
337     {
338         p_var->i_usage--;
339         vlc_mutex_unlock( &p_priv->var_lock );
340         return VLC_SUCCESS;
341     }
342
343     /* Free value if needed */
344     p_var->pf_free( &p_var->val );
345
346     /* Free choice list if needed */
347     if( p_var->choices.i_count )
348     {
349         for( i = 0 ; i < p_var->choices.i_count ; i++ )
350         {
351             p_var->pf_free( &p_var->choices.p_values[i] );
352             if( p_var->choices_text.p_values[i].psz_string )
353                 free( p_var->choices_text.p_values[i].psz_string );
354         }
355         free( p_var->choices.p_values );
356         free( p_var->choices_text.p_values );
357     }
358
359     /* Free callbacks if needed */
360     if( p_var->p_entries )
361     {
362         free( p_var->p_entries );
363     }
364
365     free( p_var->psz_name );
366     if( p_var->psz_text ) free( p_var->psz_text );
367
368     memmove( p_priv->p_vars + i_var,
369              p_priv->p_vars + i_var + 1,
370              (p_priv->i_vars - i_var - 1) * sizeof(variable_t) );
371
372     if( (p_priv->i_vars & 15) == 0 )
373     {
374         p_priv->p_vars = realloc( p_priv->p_vars,
375                           (p_priv->i_vars) * sizeof( variable_t ) );
376     }
377
378     p_priv->i_vars--;
379
380     vlc_mutex_unlock( &p_priv->var_lock );
381
382     return VLC_SUCCESS;
383 }
384
385 /**
386  * Perform an action on a variable
387  *
388  * \param p_this The object that holds the variable
389  * \param psz_name The name of the variable
390  * \param i_action The action to perform. Must be one of \ref var_action
391  * \param p_val First action parameter
392  * \param p_val2 Second action parameter
393  */
394 int __var_Change( vlc_object_t *p_this, const char *psz_name,
395                   int i_action, vlc_value_t *p_val, vlc_value_t *p_val2 )
396 {
397     int i_var, i;
398     variable_t *p_var;
399     vlc_value_t oldval;
400     vlc_object_internals_t *p_priv = p_this->p_internals;
401
402     vlc_mutex_lock( &p_priv->var_lock );
403
404     i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
405
406     if( i_var < 0 )
407     {
408         vlc_mutex_unlock( &p_priv->var_lock );
409         return VLC_ENOVAR;
410     }
411
412     p_var = &p_priv->p_vars[i_var];
413
414     switch( i_action )
415     {
416         case VLC_VAR_SETMIN:
417             if( p_var->i_type & VLC_VAR_HASMIN )
418             {
419                 p_var->pf_free( &p_var->min );
420             }
421             p_var->i_type |= VLC_VAR_HASMIN;
422             p_var->min = *p_val;
423             p_var->pf_dup( &p_var->min );
424             CheckValue( p_var, &p_var->val );
425             break;
426         case VLC_VAR_SETMAX:
427             if( p_var->i_type & VLC_VAR_HASMAX )
428             {
429                 p_var->pf_free( &p_var->max );
430             }
431             p_var->i_type |= VLC_VAR_HASMAX;
432             p_var->max = *p_val;
433             p_var->pf_dup( &p_var->max );
434             CheckValue( p_var, &p_var->val );
435             break;
436         case VLC_VAR_SETSTEP:
437             if( p_var->i_type & VLC_VAR_HASSTEP )
438             {
439                 p_var->pf_free( &p_var->step );
440             }
441             p_var->i_type |= VLC_VAR_HASSTEP;
442             p_var->step = *p_val;
443             p_var->pf_dup( &p_var->step );
444             CheckValue( p_var, &p_var->val );
445             break;
446         case VLC_VAR_ADDCHOICE:
447             /* FIXME: the list is sorted, dude. Use something cleverer. */
448             for( i = p_var->choices.i_count ; i-- ; )
449             {
450                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) < 0 )
451                 {
452                     break;
453                 }
454             }
455
456             /* The new place is i+1 */
457             i++;
458
459             if( p_var->i_default >= i )
460             {
461                 p_var->i_default++;
462             }
463
464             INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
465                          i, *p_val );
466             INSERT_ELEM( p_var->choices_text.p_values,
467                          p_var->choices_text.i_count, i, *p_val );
468             p_var->pf_dup( &p_var->choices.p_values[i] );
469             p_var->choices_text.p_values[i].psz_string =
470                 ( p_val2 && p_val2->psz_string ) ?
471                 strdup( p_val2->psz_string ) : NULL;
472
473             CheckValue( p_var, &p_var->val );
474             break;
475         case VLC_VAR_DELCHOICE:
476             /* FIXME: the list is sorted, dude. Use something cleverer. */
477             for( i = 0 ; i < p_var->choices.i_count ; i++ )
478             {
479                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
480                 {
481                     break;
482                 }
483             }
484
485             if( i == p_var->choices.i_count )
486             {
487                 /* Not found */
488                 vlc_mutex_unlock( &p_priv->var_lock );
489                 return VLC_EGENERIC;
490             }
491
492             if( p_var->i_default > i )
493             {
494                 p_var->i_default--;
495             }
496             else if( p_var->i_default == i )
497             {
498                 p_var->i_default = -1;
499             }
500
501             p_var->pf_free( &p_var->choices.p_values[i] );
502             if( p_var->choices_text.p_values[i].psz_string )
503                 free( p_var->choices_text.p_values[i].psz_string );
504             REMOVE_ELEM( p_var->choices.p_values, p_var->choices.i_count, i );
505             REMOVE_ELEM( p_var->choices_text.p_values,
506                          p_var->choices_text.i_count, i );
507
508             CheckValue( p_var, &p_var->val );
509             break;
510         case VLC_VAR_CHOICESCOUNT:
511             p_val->i_int = p_var->choices.i_count;
512             break;
513         case VLC_VAR_CLEARCHOICES:
514             for( i = 0 ; i < p_var->choices.i_count ; i++ )
515             {
516                 p_var->pf_free( &p_var->choices.p_values[i] );
517             }
518             for( i = 0 ; i < p_var->choices_text.i_count ; i++ )
519             {
520                 if( p_var->choices_text.p_values[i].psz_string )
521                     free( p_var->choices_text.p_values[i].psz_string );
522             }
523             if( p_var->choices.i_count ) free( p_var->choices.p_values );
524             if( p_var->choices_text.i_count ) free( p_var->choices_text.p_values );
525
526             p_var->choices.i_count = 0;
527             p_var->choices.p_values = NULL;
528             p_var->choices_text.i_count = 0;
529             p_var->choices_text.p_values = NULL;
530             p_var->i_default = -1;
531             break;
532         case VLC_VAR_SETDEFAULT:
533             /* FIXME: the list is sorted, dude. Use something cleverer. */
534             for( i = 0 ; i < p_var->choices.i_count ; i++ )
535             {
536                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
537                 {
538                     break;
539                 }
540             }
541
542             if( i == p_var->choices.i_count )
543             {
544                 /* Not found */
545                 break;
546             }
547
548             p_var->i_default = i;
549             CheckValue( p_var, &p_var->val );
550             break;
551         case VLC_VAR_SETVALUE:
552             /* Duplicate data if needed */
553             p_var->pf_dup( p_val );
554             /* Backup needed stuff */
555             oldval = p_var->val;
556             /* Check boundaries and list */
557             CheckValue( p_var, p_val );
558             /* Set the variable */
559             p_var->val = *p_val;
560             /* Free data if needed */
561             p_var->pf_free( &oldval );
562             break;
563         case VLC_VAR_GETCHOICES:
564         case VLC_VAR_GETLIST:
565             p_val->p_list = malloc( sizeof(vlc_list_t) );
566             if( p_val2 ) p_val2->p_list = malloc( sizeof(vlc_list_t) );
567             if( p_var->choices.i_count )
568             {
569                 p_val->p_list->p_values = malloc( p_var->choices.i_count
570                                                   * sizeof(vlc_value_t) );
571                 p_val->p_list->pi_types = malloc( p_var->choices.i_count
572                                                   * sizeof(int) );
573                 if( p_val2 )
574                 {
575                     p_val2->p_list->p_values =
576                         malloc( p_var->choices.i_count * sizeof(vlc_value_t) );
577                     p_val2->p_list->pi_types =
578                         malloc( p_var->choices.i_count * sizeof(int) );
579                 }
580             }
581             p_val->p_list->i_count = p_var->choices.i_count;
582             if( p_val2 ) p_val2->p_list->i_count = p_var->choices.i_count;
583             for( i = 0 ; i < p_var->choices.i_count ; i++ )
584             {
585                 p_val->p_list->p_values[i] = p_var->choices.p_values[i];
586                 p_val->p_list->pi_types[i] = p_var->i_type;
587                 p_var->pf_dup( &p_val->p_list->p_values[i] );
588                 if( p_val2 )
589                 {
590                     p_val2->p_list->p_values[i].psz_string =
591                         p_var->choices_text.p_values[i].psz_string ?
592                     strdup(p_var->choices_text.p_values[i].psz_string) : NULL;
593                     p_val2->p_list->pi_types[i] = VLC_VAR_STRING;
594                 }
595             }
596             break;
597         case VLC_VAR_FREELIST:
598             FreeList( p_val );
599             if( p_val2 && p_val2->p_list )
600             {
601                 for( i = 0; i < p_val2->p_list->i_count; i++ )
602                     if( p_val2->p_list->p_values[i].psz_string )
603                         free( p_val2->p_list->p_values[i].psz_string );
604                 if( p_val2->p_list->i_count )
605                 {
606                     free( p_val2->p_list->p_values );
607                     free( p_val2->p_list->pi_types );
608                 }
609                 free( p_val2->p_list );
610             }
611             break;
612         case VLC_VAR_SETTEXT:
613             if( p_var->psz_text ) free( p_var->psz_text );
614             if( p_val && p_val->psz_string )
615                 p_var->psz_text = strdup( p_val->psz_string );
616             break;
617         case VLC_VAR_GETTEXT:
618             p_val->psz_string = NULL;
619             if( p_var->psz_text )
620             {
621                 p_val->psz_string = strdup( p_var->psz_text );
622             }
623             break;
624         case VLC_VAR_INHERITVALUE:
625             {
626                 vlc_value_t val;
627
628                 if( InheritValue( p_this, psz_name, &val, p_var->i_type )
629                     == VLC_SUCCESS )
630                 {
631                     /* Duplicate already done */
632
633                     /* Backup needed stuff */
634                     oldval = p_var->val;
635                     /* Check boundaries and list */
636                     CheckValue( p_var, &val );
637                     /* Set the variable */
638                     p_var->val = val;
639                     /* Free data if needed */
640                     p_var->pf_free( &oldval );
641                 }
642
643                 if( p_val )
644                 {
645                     *p_val = p_var->val;
646                     p_var->pf_dup( p_val );
647                 }
648             }
649             break;
650         case VLC_VAR_TRIGGER_CALLBACKS:
651             {
652                 /* Deal with callbacks. Tell we're in a callback, release the lock,
653                  * call stored functions, retake the lock. */
654                 if( p_var->i_entries )
655                 {
656                     int i_var;
657                     int i_entries = p_var->i_entries;
658                     callback_entry_t *p_entries = p_var->p_entries;
659
660                     p_var->b_incallback = VLC_TRUE;
661                     vlc_mutex_unlock( &p_priv->var_lock );
662
663                     /* The real calls */
664                     for( ; i_entries-- ; )
665                     {
666                         p_entries[i_entries].pf_callback( p_this, psz_name, p_var->val, p_var->val,
667                                                           p_entries[i_entries].p_data );
668                     }
669
670                     vlc_mutex_lock( &p_priv->var_lock );
671
672                     i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
673                     if( i_var < 0 )
674                     {
675                         msg_Err( p_this, "variable %s has disappeared", psz_name );
676                         vlc_mutex_unlock( &p_priv->var_lock );
677                         return VLC_ENOVAR;
678                     }
679
680                     p_var = &p_priv->p_vars[i_var];
681                     p_var->b_incallback = VLC_FALSE;
682                 }
683             }
684             break;
685
686         default:
687             break;
688     }
689
690     vlc_mutex_unlock( &p_priv->var_lock );
691
692     return VLC_SUCCESS;
693 }
694
695 /**
696  * Request a variable's type
697  *
698  * \return The variable type if it exists, or 0 if the
699  * variable could not be found.
700  * \see \ref var_type
701  */
702 int __var_Type( vlc_object_t *p_this, const char *psz_name )
703 {
704     int i_var, i_type;
705     vlc_object_internals_t *p_priv = p_this->p_internals;
706
707     vlc_mutex_lock( &p_priv->var_lock );
708
709     i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
710
711     if( i_var < 0 )
712     {
713         vlc_mutex_unlock( &p_priv->var_lock );
714         return 0;
715     }
716
717     i_type = p_priv->p_vars[i_var].i_type;
718
719     vlc_mutex_unlock( &p_priv->var_lock );
720
721     return i_type;
722 }
723
724 /**
725  * Set a variable's value
726  *
727  * \param p_this The object that hold the variable
728  * \param psz_name The name of the variable
729  * \param val the value to set
730  */
731 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
732 {
733     int i_var;
734     variable_t *p_var;
735     vlc_value_t oldval;
736     vlc_object_internals_t *p_priv = p_this->p_internals;
737
738     vlc_mutex_lock( &p_priv->var_lock );
739
740     i_var = GetUnused( p_this, psz_name );
741     if( i_var < 0 )
742     {
743         vlc_mutex_unlock( &p_priv->var_lock );
744         return i_var;
745     }
746
747     p_var = &p_priv->p_vars[i_var];
748
749     /* Duplicate data if needed */
750     p_var->pf_dup( &val );
751
752     /* Backup needed stuff */
753     oldval = p_var->val;
754
755     /* Check boundaries and list */
756     CheckValue( p_var, &val );
757
758     /* Set the variable */
759     p_var->val = val;
760
761     /* Deal with callbacks. Tell we're in a callback, release the lock,
762      * call stored functions, retake the lock. */
763     if( p_var->i_entries )
764     {
765         int i_var;
766         int i_entries = p_var->i_entries;
767         callback_entry_t *p_entries = p_var->p_entries;
768
769         p_var->b_incallback = VLC_TRUE;
770         vlc_mutex_unlock( &p_priv->var_lock );
771
772         /* The real calls */
773         for( ; i_entries-- ; )
774         {
775             p_entries[i_entries].pf_callback( p_this, psz_name, oldval, val,
776                                               p_entries[i_entries].p_data );
777         }
778
779         vlc_mutex_lock( &p_priv->var_lock );
780
781         i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
782         if( i_var < 0 )
783         {
784             msg_Err( p_this, "variable %s has disappeared", psz_name );
785             vlc_mutex_unlock( &p_priv->var_lock );
786             return VLC_ENOVAR;
787         }
788
789         p_var = &p_priv->p_vars[i_var];
790         p_var->b_incallback = VLC_FALSE;
791     }
792
793     /* Free data if needed */
794     p_var->pf_free( &oldval );
795
796     vlc_mutex_unlock( &p_priv->var_lock );
797
798     return VLC_SUCCESS;
799 }
800
801 /**
802  * Get a variable's value
803  *
804  * \param p_this The object that holds the variable
805  * \param psz_name The name of the variable
806  * \param p_val Pointer to a vlc_value_t that will hold the variable's value
807  *              after the function is finished
808  */
809 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
810 {
811     int i_var;
812     variable_t *p_var;
813     vlc_object_internals_t *p_priv = p_this->p_internals;
814
815     vlc_mutex_lock( &p_priv->var_lock );
816
817     i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
818
819     if( i_var < 0 )
820     {
821         vlc_mutex_unlock( &p_priv->var_lock );
822         return VLC_ENOVAR;
823     }
824
825     p_var = &p_priv->p_vars[i_var];
826
827     /* Really get the variable */
828     *p_val = p_var->val;
829
830     /* Duplicate value if needed */
831     p_var->pf_dup( p_val );
832
833     vlc_mutex_unlock( &p_priv->var_lock );
834
835     return VLC_SUCCESS;
836 }
837
838
839 /**
840  * Gets a process-wide mutex, creates it if needed.
841  */
842 vlc_mutex_t *var_GetGlobalMutex( const char *name )
843 {
844     libvlc_global_data_t *p_global = vlc_global();
845     vlc_value_t val;
846
847     if( var_Create( p_global, name, VLC_VAR_MUTEX ) )
848         return NULL;
849
850     var_Get( p_global, name, &val );
851     return val.p_address;
852 }
853
854
855 /**
856  * Register a callback in a variable
857  *
858  * We store a function pointer that will be called upon variable
859  * modification.
860  *
861  * \param p_this The object that holds the variable
862  * \param psz_name The name of the variable
863  * \param pf_callback The function pointer
864  * \param p_data A generic pointer that will be passed as the last
865  *               argument to the callback function.
866  *
867  * \warning The callback function is run in the thread that calls var_Set on
868  *          the variable. Use proper locking. This thread may not have much
869  *          time to spare, so keep callback functions short.
870  */
871 int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
872                        vlc_callback_t pf_callback, void *p_data )
873 {
874     int i_var;
875     variable_t *p_var;
876     callback_entry_t entry;
877     vlc_object_internals_t *p_priv = p_this->p_internals;
878
879     entry.pf_callback = pf_callback;
880     entry.p_data = p_data;
881
882     vlc_mutex_lock( &p_priv->var_lock );
883
884     i_var = GetUnused( p_this, psz_name );
885     if( i_var < 0 )
886     {
887         vlc_mutex_unlock( &p_priv->var_lock );
888         return i_var;
889     }
890
891     p_var = &p_priv->p_vars[i_var];
892
893     INSERT_ELEM( p_var->p_entries,
894                  p_var->i_entries,
895                  p_var->i_entries,
896                  entry );
897
898     vlc_mutex_unlock( &p_priv->var_lock );
899
900     return VLC_SUCCESS;
901 }
902
903 /**
904  * Remove a callback from a variable
905  *
906  * pf_callback and p_data have to be given again, because different objects
907  * might have registered the same callback function.
908  */
909 int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
910                        vlc_callback_t pf_callback, void *p_data )
911 {
912     int i_entry, i_var;
913     variable_t *p_var;
914     vlc_object_internals_t *p_priv = p_this->p_internals;
915
916     vlc_mutex_lock( &p_priv->var_lock );
917
918     i_var = GetUnused( p_this, psz_name );
919     if( i_var < 0 )
920     {
921         vlc_mutex_unlock( &p_priv->var_lock );
922         return i_var;
923     }
924
925     p_var = &p_priv->p_vars[i_var];
926
927     for( i_entry = p_var->i_entries ; i_entry-- ; )
928     {
929         if( p_var->p_entries[i_entry].pf_callback == pf_callback
930             && p_var->p_entries[i_entry].p_data == p_data )
931         {
932             break;
933         }
934     }
935
936     if( i_entry < 0 )
937     {
938         vlc_mutex_unlock( &p_priv->var_lock );
939         return VLC_EGENERIC;
940     }
941
942     REMOVE_ELEM( p_var->p_entries, p_var->i_entries, i_entry );
943
944     vlc_mutex_unlock( &p_priv->var_lock );
945
946     return VLC_SUCCESS;
947 }
948
949 /**
950  * Trigger callback on a variable
951  *
952  * \param p_this The object that hold the variable
953  * \param psz_name The name of the variable
954  */
955 int __var_TriggerCallback( vlc_object_t *p_this, const char *psz_name )
956 {
957     int i_var;
958     variable_t *p_var;
959     vlc_value_t oldval;
960     vlc_object_internals_t *p_priv = p_this->p_internals;
961
962     vlc_mutex_lock( &p_priv->var_lock );
963
964     i_var = GetUnused( p_this, psz_name );
965     if( i_var < 0 )
966     {
967         vlc_mutex_unlock( &p_priv->var_lock );
968         return i_var;
969     }
970
971     p_var = &p_priv->p_vars[i_var];
972
973     /* Backup needed stuff */
974     oldval = p_var->val;
975
976     /* Deal with callbacks. Tell we're in a callback, release the lock,
977      * call stored functions, retake the lock. */
978     if( p_var->i_entries )
979     {
980         int i_var;
981         int i_entries = p_var->i_entries;
982         callback_entry_t *p_entries = p_var->p_entries;
983
984         p_var->b_incallback = VLC_TRUE;
985         vlc_mutex_unlock( &p_priv->var_lock );
986
987         /* The real calls */
988         for( ; i_entries-- ; )
989         {
990             p_entries[i_entries].pf_callback( p_this, psz_name, oldval, oldval,
991                                               p_entries[i_entries].p_data );
992         }
993
994         vlc_mutex_lock( &p_priv->var_lock );
995
996         i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
997         if( i_var < 0 )
998         {
999             msg_Err( p_this, "variable %s has disappeared", psz_name );
1000             vlc_mutex_unlock( &p_priv->var_lock );
1001             return VLC_ENOVAR;
1002         }
1003
1004         p_var = &p_priv->p_vars[i_var];
1005         p_var->b_incallback = VLC_FALSE;
1006     }
1007
1008     vlc_mutex_unlock( &p_priv->var_lock );
1009     return VLC_SUCCESS;
1010 }
1011
1012 /** Parse a stringified option
1013  * This function parse a string option and create the associated object
1014  * variable
1015  * The option must be of the form "[no[-]]foo[=bar]" where foo is the
1016  * option name and bar is the value of the option.
1017  * \param p_obj the object in which the variable must be created
1018  * \param psz_option the option to parse
1019  * \return nothing
1020  */
1021 void __var_OptionParse( vlc_object_t *p_obj, const char *psz_option )
1022 {
1023     char *psz_name, *psz_value = strchr( psz_option, '=' );
1024     int  i_name_len, i_type;
1025     vlc_bool_t b_isno = VLC_FALSE;
1026     vlc_value_t val;
1027
1028     if( psz_value ) i_name_len = psz_value - psz_option;
1029     else i_name_len = strlen( psz_option );
1030
1031     /* It's too much of a hassle to remove the ':' when we parse
1032      * the cmd line :) */
1033     if( i_name_len && *psz_option == ':' )
1034     {
1035         psz_option++;
1036         i_name_len--;
1037     }
1038
1039     if( i_name_len == 0 ) return;
1040
1041     psz_name = strndup( psz_option, i_name_len );
1042     if( psz_value ) psz_value++;
1043
1044     /* FIXME: :programs should be handled generically */
1045     if( !strcmp( psz_name, "programs" ) )
1046         i_type = VLC_VAR_LIST;
1047     else
1048         i_type = config_GetType( p_obj, psz_name );
1049
1050     if( !i_type && !psz_value )
1051     {
1052         /* check for "no-foo" or "nofoo" */
1053         if( !strncmp( psz_name, "no-", 3 ) )
1054         {
1055             memmove( psz_name, psz_name + 3, strlen(psz_name) + 1 - 3 );
1056         }
1057         else if( !strncmp( psz_name, "no", 2 ) )
1058         {
1059             memmove( psz_name, psz_name + 2, strlen(psz_name) + 1 - 2 );
1060         }
1061         else goto cleanup;           /* Option doesn't exist */
1062
1063         b_isno = VLC_TRUE;
1064         i_type = config_GetType( p_obj, psz_name );
1065
1066         if( !i_type ) goto cleanup;  /* Option doesn't exist */
1067     }
1068     else if( !i_type ) goto cleanup; /* Option doesn't exist */
1069
1070     if( ( i_type != VLC_VAR_BOOL ) &&
1071         ( !psz_value || !*psz_value ) ) goto cleanup; /* Invalid value */
1072
1073     /* Create the variable in the input object.
1074      * Children of the input object will be able to retreive this value
1075      * thanks to the inheritance property of the object variables. */
1076     var_Create( p_obj, psz_name, i_type );
1077
1078     switch( i_type )
1079     {
1080     case VLC_VAR_BOOL:
1081         val.b_bool = !b_isno;
1082         break;
1083
1084     case VLC_VAR_INTEGER:
1085         val.i_int = strtol( psz_value, NULL, 0 );
1086         break;
1087
1088     case VLC_VAR_FLOAT:
1089         val.f_float = atof( psz_value );
1090         break;
1091
1092     case VLC_VAR_STRING:
1093     case VLC_VAR_MODULE:
1094     case VLC_VAR_FILE:
1095     case VLC_VAR_DIRECTORY:
1096         val.psz_string = psz_value;
1097         break;
1098
1099     case VLC_VAR_LIST:
1100     {
1101         char *psz_orig, *psz_var;
1102         vlc_list_t *p_list = malloc(sizeof(vlc_list_t));
1103         val.p_list = p_list;
1104         p_list->i_count = 0;
1105
1106         psz_var = psz_orig = strdup(psz_value);
1107         while( psz_var && *psz_var )
1108         {
1109             char *psz_item = psz_var;
1110             vlc_value_t val2;
1111             while( *psz_var && *psz_var != ',' ) psz_var++;
1112             if( *psz_var == ',' )
1113             {
1114                 *psz_var = '\0';
1115                 psz_var++;
1116             }
1117             val2.i_int = strtol( psz_item, NULL, 0 );
1118             INSERT_ELEM( p_list->p_values, p_list->i_count,
1119                          p_list->i_count, val2 );
1120             /* p_list->i_count is incremented twice by INSERT_ELEM */
1121             p_list->i_count--;
1122             INSERT_ELEM( p_list->pi_types, p_list->i_count,
1123                          p_list->i_count, VLC_VAR_INTEGER );
1124         }
1125         if( psz_orig ) free( psz_orig );
1126         break;
1127     }
1128
1129     default:
1130         goto cleanup;
1131         break;
1132     }
1133
1134     var_Set( p_obj, psz_name, val );
1135
1136   cleanup:
1137     if( psz_name ) free( psz_name );
1138     return;
1139 }
1140
1141
1142 /* Following functions are local */
1143
1144 /*****************************************************************************
1145  * GetUnused: find an unused variable from its name
1146  *****************************************************************************
1147  * We do i_tries tries before giving up, just in case the variable is being
1148  * modified and called from a callback.
1149  *****************************************************************************/
1150 static int GetUnused( vlc_object_t *p_this, const char *psz_name )
1151 {
1152     int i_var, i_tries = 0;
1153     vlc_object_internals_t *p_priv = p_this->p_internals;
1154
1155     while( VLC_TRUE )
1156     {
1157         i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
1158         if( i_var < 0 )
1159         {
1160             return VLC_ENOVAR;
1161         }
1162
1163         if( ! p_priv->p_vars[i_var].b_incallback )
1164         {
1165             return i_var;
1166         }
1167
1168         if( i_tries++ > 100 )
1169         {
1170             msg_Err( p_this, "caught in a callback deadlock?" );
1171             return VLC_ETIMEOUT;
1172         }
1173
1174         vlc_mutex_unlock( &p_priv->var_lock );
1175         msleep( THREAD_SLEEP );
1176         vlc_mutex_lock( &p_priv->var_lock );
1177     }
1178 }
1179
1180 /*****************************************************************************
1181  * HashString: our cool hash function
1182  *****************************************************************************
1183  * This function is not intended to be crypto-secure, we only want it to be
1184  * fast and not suck too much. This one is pretty fast and did 0 collisions
1185  * in wenglish's dictionary.
1186  *****************************************************************************/
1187 static uint32_t HashString( const char *psz_string )
1188 {
1189     uint32_t i_hash = 0;
1190
1191     while( *psz_string )
1192     {
1193         i_hash += *psz_string++;
1194         i_hash += i_hash << 10;
1195         i_hash ^= i_hash >> 8;
1196     }
1197
1198     return i_hash;
1199 }
1200
1201 /*****************************************************************************
1202  * Insert: find an empty slot to insert a new variable
1203  *****************************************************************************
1204  * We use a recursive inner function indexed on the hash. This function does
1205  * nothing in the rare cases where a collision may occur, see Lookup()
1206  * to see how we handle them.
1207  * XXX: does this really need to be written recursively?
1208  *****************************************************************************/
1209 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
1210 {
1211     if( i_count == 0 )
1212     {
1213         return 0;
1214     }
1215
1216     return InsertInner( p_vars, i_count, HashString( psz_name ) );
1217 }
1218
1219 static int InsertInner( variable_t *p_vars, int i_count, uint32_t i_hash )
1220 {
1221     int i_middle;
1222
1223     if( i_hash <= p_vars[0].i_hash )
1224     {
1225         return 0;
1226     }
1227
1228     if( i_hash >= p_vars[i_count - 1].i_hash )
1229     {
1230         return i_count;
1231     }
1232
1233     i_middle = i_count / 2;
1234
1235     /* We know that 0 < i_middle */
1236     if( i_hash < p_vars[i_middle].i_hash )
1237     {
1238         return InsertInner( p_vars, i_middle, i_hash );
1239     }
1240
1241     /* We know that i_middle + 1 < i_count */
1242     if( i_hash > p_vars[i_middle + 1].i_hash )
1243     {
1244         return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
1245                                            i_count - i_middle - 1,
1246                                            i_hash );
1247     }
1248
1249     return i_middle + 1;
1250 }
1251
1252 /*****************************************************************************
1253  * Lookup: find an existing variable given its name
1254  *****************************************************************************
1255  * We use a recursive inner function indexed on the hash. Care is taken of
1256  * possible hash collisions.
1257  * XXX: does this really need to be written recursively?
1258  *****************************************************************************/
1259 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
1260 {
1261     uint32_t i_hash;
1262     int i, i_pos;
1263
1264     if( i_count == 0 )
1265     {
1266         return -1;
1267     }
1268
1269     i_hash = HashString( psz_name );
1270
1271     i_pos = LookupInner( p_vars, i_count, i_hash );
1272
1273     /* Hash not found */
1274     if( i_hash != p_vars[i_pos].i_hash )
1275     {
1276         return -1;
1277     }
1278
1279     /* Hash found, entry found */
1280     if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
1281     {
1282         return i_pos;
1283     }
1284
1285     /* Hash collision! This should be very rare, but we cannot guarantee
1286      * it will never happen. Just do an exhaustive search amongst all
1287      * entries with the same hash. */
1288     for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
1289     {
1290         if( !strcmp( psz_name, p_vars[i].psz_name ) )
1291         {
1292             return i;
1293         }
1294     }
1295
1296     for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
1297     {
1298         if( !strcmp( psz_name, p_vars[i].psz_name ) )
1299         {
1300             return i;
1301         }
1302     }
1303
1304     /* Hash found, but entry not found */
1305     return -1;
1306 }
1307
1308 static int LookupInner( variable_t *p_vars, int i_count, uint32_t i_hash )
1309 {
1310     int i_middle;
1311
1312     if( i_hash <= p_vars[0].i_hash )
1313     {
1314         return 0;
1315     }
1316
1317     if( i_hash >= p_vars[i_count-1].i_hash )
1318     {
1319         return i_count - 1;
1320     }
1321
1322     i_middle = i_count / 2;
1323
1324     /* We know that 0 < i_middle */
1325     if( i_hash < p_vars[i_middle].i_hash )
1326     {
1327         return LookupInner( p_vars, i_middle, i_hash );
1328     }
1329
1330     /* We know that i_middle + 1 < i_count */
1331     if( i_hash > p_vars[i_middle].i_hash )
1332     {
1333         return i_middle + LookupInner( p_vars + i_middle,
1334                                        i_count - i_middle,
1335                                        i_hash );
1336     }
1337
1338     return i_middle;
1339 }
1340
1341 /*****************************************************************************
1342  * CheckValue: check that a value is valid wrt. a variable
1343  *****************************************************************************
1344  * This function checks p_val's value against p_var's limitations such as
1345  * minimal and maximal value, step, in-list position, and modifies p_val if
1346  * necessary.
1347  ****************************************************************************/
1348 static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
1349 {
1350     /* Check that our variable is in the list */
1351     if( p_var->i_type & VLC_VAR_HASCHOICE && p_var->choices.i_count )
1352     {
1353         int i;
1354
1355         /* FIXME: the list is sorted, dude. Use something cleverer. */
1356         for( i = p_var->choices.i_count ; i-- ; )
1357         {
1358             if( p_var->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
1359             {
1360                 break;
1361             }
1362         }
1363
1364         /* If not found, change it to anything vaguely valid */
1365         if( i < 0 )
1366         {
1367             /* Free the old variable, get the new one, dup it */
1368             p_var->pf_free( p_val );
1369             *p_val = p_var->choices.p_values[p_var->i_default >= 0
1370                                           ? p_var->i_default : 0 ];
1371             p_var->pf_dup( p_val );
1372         }
1373     }
1374
1375     /* Check that our variable is within the bounds */
1376     switch( p_var->i_type & VLC_VAR_TYPE )
1377     {
1378         case VLC_VAR_INTEGER:
1379             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.i_int
1380                  && (p_val->i_int % p_var->step.i_int) )
1381             {
1382                 p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
1383                                / p_var->step.i_int * p_var->step.i_int;
1384             }
1385             if( p_var->i_type & VLC_VAR_HASMIN
1386                  && p_val->i_int < p_var->min.i_int )
1387             {
1388                 p_val->i_int = p_var->min.i_int;
1389             }
1390             if( p_var->i_type & VLC_VAR_HASMAX
1391                  && p_val->i_int > p_var->max.i_int )
1392             {
1393                 p_val->i_int = p_var->max.i_int;
1394             }
1395             break;
1396         case VLC_VAR_FLOAT:
1397             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.f_float )
1398             {
1399                 float f_round = p_var->step.f_float * (float)(int)( 0.5 +
1400                                         p_val->f_float / p_var->step.f_float );
1401                 if( p_val->f_float != f_round )
1402                 {
1403                     p_val->f_float = f_round;
1404                 }
1405             }
1406             if( p_var->i_type & VLC_VAR_HASMIN
1407                  && p_val->f_float < p_var->min.f_float )
1408             {
1409                 p_val->f_float = p_var->min.f_float;
1410             }
1411             if( p_var->i_type & VLC_VAR_HASMAX
1412                  && p_val->f_float > p_var->max.f_float )
1413             {
1414                 p_val->f_float = p_var->max.f_float;
1415             }
1416             break;
1417         case VLC_VAR_TIME:
1418             /* FIXME: TODO */
1419             break;
1420     }
1421 }
1422
1423 /*****************************************************************************
1424  * InheritValue: try to inherit the value of this variable from the same one
1425  *               in our closest parent.
1426  *****************************************************************************/
1427 static int InheritValue( vlc_object_t *p_this, const char *psz_name,
1428                          vlc_value_t *p_val, int i_type )
1429 {
1430     int i_var;
1431     variable_t *p_var;
1432
1433     /* No need to take the structure lock,
1434      * we are only looking for our parents */
1435
1436     if( !p_this->p_parent )
1437     {
1438         switch( i_type & VLC_VAR_TYPE )
1439         {
1440         case VLC_VAR_FILE:
1441         case VLC_VAR_DIRECTORY:
1442         case VLC_VAR_STRING:
1443         case VLC_VAR_MODULE:
1444             p_val->psz_string = config_GetPsz( p_this, psz_name );
1445             if( !p_val->psz_string ) p_val->psz_string = strdup("");
1446             break;
1447         case VLC_VAR_FLOAT:
1448             p_val->f_float = config_GetFloat( p_this, psz_name );
1449             break;
1450         case VLC_VAR_INTEGER:
1451         case VLC_VAR_HOTKEY:
1452             p_val->i_int = config_GetInt( p_this, psz_name );
1453             break;
1454         case VLC_VAR_BOOL:
1455             p_val->b_bool = config_GetInt( p_this, psz_name );
1456             break;
1457         case VLC_VAR_LIST:
1458         {
1459             char *psz_orig, *psz_var;
1460             vlc_list_t *p_list = malloc(sizeof(vlc_list_t));
1461             p_val->p_list = p_list;
1462             p_list->i_count = 0;
1463
1464             psz_var = psz_orig = config_GetPsz( p_this, psz_name );
1465             while( psz_var && *psz_var )
1466             {
1467                 char *psz_item = psz_var;
1468                 vlc_value_t val;
1469                 while( *psz_var && *psz_var != ',' ) psz_var++;
1470                 if( *psz_var == ',' )
1471                 {
1472                     *psz_var = '\0';
1473                     psz_var++;
1474                 }
1475                 val.i_int = strtol( psz_item, NULL, 0 );
1476                 INSERT_ELEM( p_list->p_values, p_list->i_count,
1477                              p_list->i_count, val );
1478                 /* p_list->i_count is incremented twice by INSERT_ELEM */
1479                 p_list->i_count--;
1480                 INSERT_ELEM( p_list->pi_types, p_list->i_count,
1481                              p_list->i_count, VLC_VAR_INTEGER );
1482             }
1483             if( psz_orig ) free( psz_orig );
1484             break;
1485         }
1486         default:
1487             return VLC_ENOOBJ;
1488             break;
1489         }
1490
1491         return VLC_SUCCESS;
1492     }
1493
1494     vlc_object_internals_t *p_priv = p_this->p_parent->p_internals;
1495
1496     /* Look for the variable */
1497     vlc_mutex_lock( &p_priv->var_lock );
1498
1499     i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name );
1500
1501     if( i_var >= 0 )
1502     {
1503         /* We found it! */
1504         p_var = &p_priv->p_vars[i_var];
1505
1506         /* Really get the variable */
1507         *p_val = p_var->val;
1508
1509         /* Duplicate value if needed */
1510         p_var->pf_dup( p_val );
1511
1512         vlc_mutex_unlock( &p_priv->var_lock );
1513         return VLC_SUCCESS;
1514     }
1515
1516     vlc_mutex_unlock( &p_priv->var_lock );
1517
1518     /* We're still not there */
1519
1520     return InheritValue( p_this->p_parent, psz_name, p_val, i_type );
1521 }
1522
1523 /**********************************************************************
1524  * Execute a var command on an object identified by its name
1525  **********************************************************************/
1526 int __var_Command( vlc_object_t *p_this, const char *psz_name,
1527                    const char *psz_cmd, const char *psz_arg, char **psz_msg )
1528 {
1529     vlc_object_t *p_obj = vlc_object_find_name( p_this->p_libvlc,
1530                                                 psz_name, FIND_CHILD );
1531     int i_type, i_ret;
1532
1533     if( !p_obj )
1534     {
1535         if( psz_msg )
1536             *psz_msg = strdup( "Unknown destination object." );
1537         return VLC_ENOOBJ;
1538     }
1539
1540     i_type = var_Type( p_obj, psz_cmd );
1541     if( !( i_type&VLC_VAR_ISCOMMAND ) )
1542     {
1543         vlc_object_release( p_obj );
1544         if( psz_msg )
1545             *psz_msg = strdup( "Variable doesn't exist or isn't a command." );
1546         return VLC_EGENERIC;
1547     }
1548
1549     i_type &= 0xf0;
1550     switch( i_type )
1551     {
1552         case VLC_VAR_INTEGER:
1553             i_ret = var_SetInteger( p_obj, psz_cmd, atoi( psz_arg ) );
1554             break;
1555         case VLC_VAR_FLOAT:
1556             i_ret = var_SetFloat( p_obj, psz_cmd, atof( psz_arg ) );
1557             break;
1558         case VLC_VAR_STRING:
1559             i_ret = var_SetString( p_obj, psz_cmd, psz_arg );
1560             break;
1561         case VLC_VAR_BOOL:
1562             i_ret = var_SetBool( p_obj, psz_cmd, atoi( psz_arg ) );
1563             break;
1564         default:
1565             i_ret = VLC_EGENERIC;
1566             break;
1567     }
1568
1569     vlc_object_release( p_obj );
1570
1571     if( psz_msg )
1572     {
1573         *psz_msg = (char*)malloc( 80 );
1574         sprintf( *psz_msg, "%s on object %s returned %i (%s)",
1575                  psz_cmd, psz_name, i_ret, vlc_error( i_ret ) );
1576     }
1577
1578     return i_ret;
1579 }