]> git.sesse.net Git - vlc/blob - src/misc/variables.c
8abba518c41d0c5ca026cd2a8f60b6e0aba0e11b
[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  * Register a callback in a variable
833  *
834  * We store a function pointer that will be called upon variable
835  * modification.
836  *
837  * \param p_this The object that holds the variable
838  * \param psz_name The name of the variable
839  * \param pf_callback The function pointer
840  * \param p_data A generic pointer that will be passed as the last
841  *               argument to the callback function.
842  *
843  * \warning The callback function is run in the thread that calls var_Set on
844  *          the variable. Use proper locking. This thread may not have much
845  *          time to spare, so keep callback functions short.
846  */
847 int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
848                        vlc_callback_t pf_callback, void *p_data )
849 {
850     int i_var;
851     variable_t *p_var;
852     callback_entry_t entry;
853
854     entry.pf_callback = pf_callback;
855     entry.p_data = p_data;
856
857     vlc_mutex_lock( &p_this->var_lock );
858
859     i_var = GetUnused( p_this, psz_name );
860     if( i_var < 0 )
861     {
862         vlc_mutex_unlock( &p_this->var_lock );
863         return i_var;
864     }
865
866     p_var = &p_this->p_vars[i_var];
867
868     INSERT_ELEM( p_var->p_entries,
869                  p_var->i_entries,
870                  p_var->i_entries,
871                  entry );
872
873     vlc_mutex_unlock( &p_this->var_lock );
874
875     return VLC_SUCCESS;
876 }
877
878 /**
879  * Remove a callback from a variable
880  *
881  * pf_callback and p_data have to be given again, because different objects
882  * might have registered the same callback function.
883  */
884 int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
885                        vlc_callback_t pf_callback, void *p_data )
886 {
887     int i_entry, i_var;
888     variable_t *p_var;
889
890     vlc_mutex_lock( &p_this->var_lock );
891
892     i_var = GetUnused( p_this, psz_name );
893     if( i_var < 0 )
894     {
895         vlc_mutex_unlock( &p_this->var_lock );
896         return i_var;
897     }
898
899     p_var = &p_this->p_vars[i_var];
900
901     for( i_entry = p_var->i_entries ; i_entry-- ; )
902     {
903         if( p_var->p_entries[i_entry].pf_callback == pf_callback
904             && p_var->p_entries[i_entry].p_data == p_data )
905         {
906             break;
907         }
908     }
909
910     if( i_entry < 0 )
911     {
912         vlc_mutex_unlock( &p_this->var_lock );
913         return VLC_EGENERIC;
914     }
915
916     REMOVE_ELEM( p_var->p_entries, p_var->i_entries, i_entry );
917
918     vlc_mutex_unlock( &p_this->var_lock );
919
920     return VLC_SUCCESS;
921 }
922
923 /**
924  * Trigger callback on a variable
925  *
926  * \param p_this The object that hold the variable
927  * \param psz_name The name of the variable
928  */
929 int __var_TriggerCallback( vlc_object_t *p_this, const char *psz_name )
930 {
931     int i_var;
932     variable_t *p_var;
933     vlc_value_t oldval;
934
935     vlc_mutex_lock( &p_this->var_lock );
936
937     i_var = GetUnused( p_this, psz_name );
938     if( i_var < 0 )
939     {
940         vlc_mutex_unlock( &p_this->var_lock );
941         return i_var;
942     }
943
944     p_var = &p_this->p_vars[i_var];
945
946     /* Backup needed stuff */
947     oldval = p_var->val;
948
949     /* Deal with callbacks. Tell we're in a callback, release the lock,
950      * call stored functions, retake the lock. */
951     if( p_var->i_entries )
952     {
953         int i_var;
954         int i_entries = p_var->i_entries;
955         callback_entry_t *p_entries = p_var->p_entries;
956
957         p_var->b_incallback = VLC_TRUE;
958         vlc_mutex_unlock( &p_this->var_lock );
959
960         /* The real calls */
961         for( ; i_entries-- ; )
962         {
963             p_entries[i_entries].pf_callback( p_this, psz_name, oldval, oldval,
964                                               p_entries[i_entries].p_data );
965         }
966
967         vlc_mutex_lock( &p_this->var_lock );
968
969         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
970         if( i_var < 0 )
971         {
972             msg_Err( p_this, "variable %s has disappeared", psz_name );
973             vlc_mutex_unlock( &p_this->var_lock );
974             return VLC_ENOVAR;
975         }
976
977         p_var = &p_this->p_vars[i_var];
978         p_var->b_incallback = VLC_FALSE;
979     }
980
981     vlc_mutex_unlock( &p_this->var_lock );
982     return VLC_SUCCESS;
983 }
984
985 /** Parse a stringified option
986  * This function parse a string option and create the associated object
987  * variable
988  * The option must be of the form "[no[-]]foo[=bar]" where foo is the
989  * option name and bar is the value of the option.
990  * \param p_obj the object in which the variable must be created
991  * \param psz_option the option to parse
992  * \return nothing
993  */
994 void __var_OptionParse( vlc_object_t *p_obj, const char *psz_option )
995 {
996     char *psz_name, *psz_value = strchr( psz_option, '=' );
997     int  i_name_len, i_type;
998     vlc_bool_t b_isno = VLC_FALSE;
999     vlc_value_t val;
1000
1001     if( psz_value ) i_name_len = psz_value - psz_option;
1002     else i_name_len = strlen( psz_option );
1003
1004     /* It's too much of a hassle to remove the ':' when we parse
1005      * the cmd line :) */
1006     if( i_name_len && *psz_option == ':' )
1007     {
1008         psz_option++;
1009         i_name_len--;
1010     }
1011
1012     if( i_name_len == 0 ) return;
1013
1014     psz_name = strndup( psz_option, i_name_len );
1015     if( psz_value ) psz_value++;
1016
1017     /* FIXME: :programs should be handled generically */
1018     if( !strcmp( psz_name, "programs" ) )
1019         i_type = VLC_VAR_LIST;
1020     else
1021         i_type = config_GetType( p_obj, psz_name );
1022
1023     if( !i_type && !psz_value )
1024     {
1025         /* check for "no-foo" or "nofoo" */
1026         if( !strncmp( psz_name, "no-", 3 ) )
1027         {
1028             memmove( psz_name, psz_name + 3, strlen(psz_name) + 1 - 3 );
1029         }
1030         else if( !strncmp( psz_name, "no", 2 ) )
1031         {
1032             memmove( psz_name, psz_name + 2, strlen(psz_name) + 1 - 2 );
1033         }
1034         else goto cleanup;           /* Option doesn't exist */
1035
1036         b_isno = VLC_TRUE;
1037         i_type = config_GetType( p_obj, psz_name );
1038
1039         if( !i_type ) goto cleanup;  /* Option doesn't exist */
1040     }
1041     else if( !i_type ) goto cleanup; /* Option doesn't exist */
1042
1043     if( ( i_type != VLC_VAR_BOOL ) &&
1044         ( !psz_value || !*psz_value ) ) goto cleanup; /* Invalid value */
1045
1046     /* Create the variable in the input object.
1047      * Children of the input object will be able to retreive this value
1048      * thanks to the inheritance property of the object variables. */
1049     var_Create( p_obj, psz_name, i_type );
1050
1051     switch( i_type )
1052     {
1053     case VLC_VAR_BOOL:
1054         val.b_bool = !b_isno;
1055         break;
1056
1057     case VLC_VAR_INTEGER:
1058         val.i_int = strtol( psz_value, NULL, 0 );
1059         break;
1060
1061     case VLC_VAR_FLOAT:
1062         val.f_float = atof( psz_value );
1063         break;
1064
1065     case VLC_VAR_STRING:
1066     case VLC_VAR_MODULE:
1067     case VLC_VAR_FILE:
1068     case VLC_VAR_DIRECTORY:
1069         val.psz_string = psz_value;
1070         break;
1071
1072     case VLC_VAR_LIST:
1073     {
1074         char *psz_orig, *psz_var;
1075         vlc_list_t *p_list = malloc(sizeof(vlc_list_t));
1076         val.p_list = p_list;
1077         p_list->i_count = 0;
1078
1079         psz_var = psz_orig = strdup(psz_value);
1080         while( psz_var && *psz_var )
1081         {
1082             char *psz_item = psz_var;
1083             vlc_value_t val2;
1084             while( *psz_var && *psz_var != ',' ) psz_var++;
1085             if( *psz_var == ',' )
1086             {
1087                 *psz_var = '\0';
1088                 psz_var++;
1089             }
1090             val2.i_int = strtol( psz_item, NULL, 0 );
1091             INSERT_ELEM( p_list->p_values, p_list->i_count,
1092                          p_list->i_count, val2 );
1093             /* p_list->i_count is incremented twice by INSERT_ELEM */
1094             p_list->i_count--;
1095             INSERT_ELEM( p_list->pi_types, p_list->i_count,
1096                          p_list->i_count, VLC_VAR_INTEGER );
1097         }
1098         if( psz_orig ) free( psz_orig );
1099         break;
1100     }
1101
1102     default:
1103         goto cleanup;
1104         break;
1105     }
1106
1107     var_Set( p_obj, psz_name, val );
1108
1109   cleanup:
1110     if( psz_name ) free( psz_name );
1111     return;
1112 }
1113
1114
1115 /* Following functions are local */
1116
1117 /*****************************************************************************
1118  * GetUnused: find an unused variable from its name
1119  *****************************************************************************
1120  * We do i_tries tries before giving up, just in case the variable is being
1121  * modified and called from a callback.
1122  *****************************************************************************/
1123 static int GetUnused( vlc_object_t *p_this, const char *psz_name )
1124 {
1125     int i_var, i_tries = 0;
1126
1127     while( VLC_TRUE )
1128     {
1129         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
1130         if( i_var < 0 )
1131         {
1132             return VLC_ENOVAR;
1133         }
1134
1135         if( ! p_this->p_vars[i_var].b_incallback )
1136         {
1137             return i_var;
1138         }
1139
1140         if( i_tries++ > 100 )
1141         {
1142             msg_Err( p_this, "caught in a callback deadlock?" );
1143             return VLC_ETIMEOUT;
1144         }
1145
1146         vlc_mutex_unlock( &p_this->var_lock );
1147         msleep( THREAD_SLEEP );
1148         vlc_mutex_lock( &p_this->var_lock );
1149     }
1150 }
1151
1152 /*****************************************************************************
1153  * HashString: our cool hash function
1154  *****************************************************************************
1155  * This function is not intended to be crypto-secure, we only want it to be
1156  * fast and not suck too much. This one is pretty fast and did 0 collisions
1157  * in wenglish's dictionary.
1158  *****************************************************************************/
1159 static uint32_t HashString( const char *psz_string )
1160 {
1161     uint32_t i_hash = 0;
1162
1163     while( *psz_string )
1164     {
1165         i_hash += *psz_string++;
1166         i_hash += i_hash << 10;
1167         i_hash ^= i_hash >> 8;
1168     }
1169
1170     return i_hash;
1171 }
1172
1173 /*****************************************************************************
1174  * Insert: find an empty slot to insert a new variable
1175  *****************************************************************************
1176  * We use a recursive inner function indexed on the hash. This function does
1177  * nothing in the rare cases where a collision may occur, see Lookup()
1178  * to see how we handle them.
1179  * XXX: does this really need to be written recursively?
1180  *****************************************************************************/
1181 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
1182 {
1183     if( i_count == 0 )
1184     {
1185         return 0;
1186     }
1187
1188     return InsertInner( p_vars, i_count, HashString( psz_name ) );
1189 }
1190
1191 static int InsertInner( variable_t *p_vars, int i_count, uint32_t i_hash )
1192 {
1193     int i_middle;
1194
1195     if( i_hash <= p_vars[0].i_hash )
1196     {
1197         return 0;
1198     }
1199
1200     if( i_hash >= p_vars[i_count - 1].i_hash )
1201     {
1202         return i_count;
1203     }
1204
1205     i_middle = i_count / 2;
1206
1207     /* We know that 0 < i_middle */
1208     if( i_hash < p_vars[i_middle].i_hash )
1209     {
1210         return InsertInner( p_vars, i_middle, i_hash );
1211     }
1212
1213     /* We know that i_middle + 1 < i_count */
1214     if( i_hash > p_vars[i_middle + 1].i_hash )
1215     {
1216         return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
1217                                            i_count - i_middle - 1,
1218                                            i_hash );
1219     }
1220
1221     return i_middle + 1;
1222 }
1223
1224 /*****************************************************************************
1225  * Lookup: find an existing variable given its name
1226  *****************************************************************************
1227  * We use a recursive inner function indexed on the hash. Care is taken of
1228  * possible hash collisions.
1229  * XXX: does this really need to be written recursively?
1230  *****************************************************************************/
1231 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
1232 {
1233     uint32_t i_hash;
1234     int i, i_pos;
1235
1236     if( i_count == 0 )
1237     {
1238         return -1;
1239     }
1240
1241     i_hash = HashString( psz_name );
1242
1243     i_pos = LookupInner( p_vars, i_count, i_hash );
1244
1245     /* Hash not found */
1246     if( i_hash != p_vars[i_pos].i_hash )
1247     {
1248         return -1;
1249     }
1250
1251     /* Hash found, entry found */
1252     if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
1253     {
1254         return i_pos;
1255     }
1256
1257     /* Hash collision! This should be very rare, but we cannot guarantee
1258      * it will never happen. Just do an exhaustive search amongst all
1259      * entries with the same hash. */
1260     for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
1261     {
1262         if( !strcmp( psz_name, p_vars[i].psz_name ) )
1263         {
1264             return i;
1265         }
1266     }
1267
1268     for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
1269     {
1270         if( !strcmp( psz_name, p_vars[i].psz_name ) )
1271         {
1272             return i;
1273         }
1274     }
1275
1276     /* Hash found, but entry not found */
1277     return -1;
1278 }
1279
1280 static int LookupInner( variable_t *p_vars, int i_count, uint32_t i_hash )
1281 {
1282     int i_middle;
1283
1284     if( i_hash <= p_vars[0].i_hash )
1285     {
1286         return 0;
1287     }
1288
1289     if( i_hash >= p_vars[i_count-1].i_hash )
1290     {
1291         return i_count - 1;
1292     }
1293
1294     i_middle = i_count / 2;
1295
1296     /* We know that 0 < i_middle */
1297     if( i_hash < p_vars[i_middle].i_hash )
1298     {
1299         return LookupInner( p_vars, i_middle, i_hash );
1300     }
1301
1302     /* We know that i_middle + 1 < i_count */
1303     if( i_hash > p_vars[i_middle].i_hash )
1304     {
1305         return i_middle + LookupInner( p_vars + i_middle,
1306                                        i_count - i_middle,
1307                                        i_hash );
1308     }
1309
1310     return i_middle;
1311 }
1312
1313 /*****************************************************************************
1314  * CheckValue: check that a value is valid wrt. a variable
1315  *****************************************************************************
1316  * This function checks p_val's value against p_var's limitations such as
1317  * minimal and maximal value, step, in-list position, and modifies p_val if
1318  * necessary.
1319  ****************************************************************************/
1320 static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
1321 {
1322     /* Check that our variable is in the list */
1323     if( p_var->i_type & VLC_VAR_HASCHOICE && p_var->choices.i_count )
1324     {
1325         int i;
1326
1327         /* FIXME: the list is sorted, dude. Use something cleverer. */
1328         for( i = p_var->choices.i_count ; i-- ; )
1329         {
1330             if( p_var->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
1331             {
1332                 break;
1333             }
1334         }
1335
1336         /* If not found, change it to anything vaguely valid */
1337         if( i < 0 )
1338         {
1339             /* Free the old variable, get the new one, dup it */
1340             p_var->pf_free( p_val );
1341             *p_val = p_var->choices.p_values[p_var->i_default >= 0
1342                                           ? p_var->i_default : 0 ];
1343             p_var->pf_dup( p_val );
1344         }
1345     }
1346
1347     /* Check that our variable is within the bounds */
1348     switch( p_var->i_type & VLC_VAR_TYPE )
1349     {
1350         case VLC_VAR_INTEGER:
1351             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.i_int
1352                  && (p_val->i_int % p_var->step.i_int) )
1353             {
1354                 p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
1355                                / p_var->step.i_int * p_var->step.i_int;
1356             }
1357             if( p_var->i_type & VLC_VAR_HASMIN
1358                  && p_val->i_int < p_var->min.i_int )
1359             {
1360                 p_val->i_int = p_var->min.i_int;
1361             }
1362             if( p_var->i_type & VLC_VAR_HASMAX
1363                  && p_val->i_int > p_var->max.i_int )
1364             {
1365                 p_val->i_int = p_var->max.i_int;
1366             }
1367             break;
1368         case VLC_VAR_FLOAT:
1369             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.f_float )
1370             {
1371                 float f_round = p_var->step.f_float * (float)(int)( 0.5 +
1372                                         p_val->f_float / p_var->step.f_float );
1373                 if( p_val->f_float != f_round )
1374                 {
1375                     p_val->f_float = f_round;
1376                 }
1377             }
1378             if( p_var->i_type & VLC_VAR_HASMIN
1379                  && p_val->f_float < p_var->min.f_float )
1380             {
1381                 p_val->f_float = p_var->min.f_float;
1382             }
1383             if( p_var->i_type & VLC_VAR_HASMAX
1384                  && p_val->f_float > p_var->max.f_float )
1385             {
1386                 p_val->f_float = p_var->max.f_float;
1387             }
1388             break;
1389         case VLC_VAR_TIME:
1390             /* FIXME: TODO */
1391             break;
1392     }
1393 }
1394
1395 /*****************************************************************************
1396  * InheritValue: try to inherit the value of this variable from the same one
1397  *               in our closest parent.
1398  *****************************************************************************/
1399 static int InheritValue( vlc_object_t *p_this, const char *psz_name,
1400                          vlc_value_t *p_val, int i_type )
1401 {
1402     int i_var;
1403     variable_t *p_var;
1404
1405     /* No need to take the structure lock,
1406      * we are only looking for our parents */
1407
1408     if( !p_this->p_parent )
1409     {
1410         switch( i_type & VLC_VAR_TYPE )
1411         {
1412         case VLC_VAR_FILE:
1413         case VLC_VAR_DIRECTORY:
1414         case VLC_VAR_STRING:
1415         case VLC_VAR_MODULE:
1416             p_val->psz_string = config_GetPsz( p_this, psz_name );
1417             if( !p_val->psz_string ) p_val->psz_string = strdup("");
1418             break;
1419         case VLC_VAR_FLOAT:
1420             p_val->f_float = config_GetFloat( p_this, psz_name );
1421             break;
1422         case VLC_VAR_INTEGER:
1423         case VLC_VAR_HOTKEY:
1424             p_val->i_int = config_GetInt( p_this, psz_name );
1425             break;
1426         case VLC_VAR_BOOL:
1427             p_val->b_bool = config_GetInt( p_this, psz_name );
1428             break;
1429         case VLC_VAR_LIST:
1430         {
1431             char *psz_orig, *psz_var;
1432             vlc_list_t *p_list = malloc(sizeof(vlc_list_t));
1433             p_val->p_list = p_list;
1434             p_list->i_count = 0;
1435
1436             psz_var = psz_orig = config_GetPsz( p_this, psz_name );
1437             while( psz_var && *psz_var )
1438             {
1439                 char *psz_item = psz_var;
1440                 vlc_value_t val;
1441                 while( *psz_var && *psz_var != ',' ) psz_var++;
1442                 if( *psz_var == ',' )
1443                 {
1444                     *psz_var = '\0';
1445                     psz_var++;
1446                 }
1447                 val.i_int = strtol( psz_item, NULL, 0 );
1448                 INSERT_ELEM( p_list->p_values, p_list->i_count,
1449                              p_list->i_count, val );
1450                 /* p_list->i_count is incremented twice by INSERT_ELEM */
1451                 p_list->i_count--;
1452                 INSERT_ELEM( p_list->pi_types, p_list->i_count,
1453                              p_list->i_count, VLC_VAR_INTEGER );
1454             }
1455             if( psz_orig ) free( psz_orig );
1456             break;
1457         }
1458         default:
1459             return VLC_ENOOBJ;
1460             break;
1461         }
1462
1463         return VLC_SUCCESS;
1464     }
1465
1466     /* Look for the variable */
1467     vlc_mutex_lock( &p_this->p_parent->var_lock );
1468
1469     i_var = Lookup( p_this->p_parent->p_vars, p_this->p_parent->i_vars,
1470                     psz_name );
1471
1472     if( i_var >= 0 )
1473     {
1474         /* We found it! */
1475         p_var = &p_this->p_parent->p_vars[i_var];
1476
1477         /* Really get the variable */
1478         *p_val = p_var->val;
1479
1480         /* Duplicate value if needed */
1481         p_var->pf_dup( p_val );
1482
1483         vlc_mutex_unlock( &p_this->p_parent->var_lock );
1484         return VLC_SUCCESS;
1485     }
1486
1487     vlc_mutex_unlock( &p_this->p_parent->var_lock );
1488
1489     /* We're still not there */
1490
1491     return InheritValue( p_this->p_parent, psz_name, p_val, i_type );
1492 }
1493
1494 /**********************************************************************
1495  * Execute a var command on an object identified by its name
1496  **********************************************************************/
1497 int __var_Command( vlc_object_t *p_this, const char *psz_name,
1498                    const char *psz_cmd, const char *psz_arg, char **psz_msg )
1499 {
1500     vlc_object_t *p_obj = vlc_object_find_name( p_this->p_libvlc,
1501                                                 psz_name, FIND_CHILD );
1502     int i_type, i_ret;
1503
1504     if( !p_obj )
1505     {
1506         if( psz_msg )
1507             *psz_msg = strdup( "Unknown destination object." );
1508         return VLC_ENOOBJ;
1509     }
1510
1511     i_type = var_Type( p_obj, psz_cmd );
1512     if( !( i_type&VLC_VAR_ISCOMMAND ) )
1513     {
1514         vlc_object_release( p_obj );
1515         if( psz_msg )
1516             *psz_msg = strdup( "Variable doesn't exist or isn't a command." );
1517         return VLC_EGENERIC;
1518     }
1519
1520     i_type &= 0xf0;
1521     switch( i_type )
1522     {
1523         case VLC_VAR_INTEGER:
1524             i_ret = var_SetInteger( p_obj, psz_cmd, atoi( psz_arg ) );
1525             break;
1526         case VLC_VAR_FLOAT:
1527             i_ret = var_SetFloat( p_obj, psz_cmd, atof( psz_arg ) );
1528             break;
1529         case VLC_VAR_STRING:
1530             i_ret = var_SetString( p_obj, psz_cmd, psz_arg );
1531             break;
1532         case VLC_VAR_BOOL:
1533             i_ret = var_SetBool( p_obj, psz_cmd, atoi( psz_arg ) );
1534             break;
1535         default:
1536             i_ret = VLC_EGENERIC;
1537             break;
1538     }
1539
1540     vlc_object_release( p_obj );
1541
1542     if( psz_msg )
1543     {
1544         *psz_msg = (char*)malloc( 80 );
1545         sprintf( *psz_msg, "%s on object %s returned %i (%s)",
1546                  psz_cmd, psz_name, i_ret, vlc_error( i_ret ) );
1547     }
1548
1549     return i_ret;
1550 }