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