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