520604ca221e39c7af936bd9c6c15c6c15c5db45
[vlc.git] / 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 /** Parse a stringified option
924  * This function parse a string option and create the associated object
925  * variable
926  * The option must be of the form "[no[-]]foo[=bar]" where foo is the
927  * option name and bar is the value of the option.
928  * \param p_obj the object in which the variable must be created
929  * \param psz_option the option to parse
930  * \return nothing
931  */
932 void __var_OptionParse( vlc_object_t *p_obj, const char *psz_option )
933 {
934     char *psz_name, *psz_value = strchr( psz_option, '=' );
935     int  i_name_len, i_type;
936     vlc_bool_t b_isno = VLC_FALSE;
937     vlc_value_t val;
938
939     if( psz_value ) i_name_len = psz_value - psz_option;
940     else i_name_len = strlen( psz_option );
941
942     /* It's too much of a hassle to remove the ':' when we parse
943      * the cmd line :) */
944     if( i_name_len && *psz_option == ':' )
945     {
946         psz_option++;
947         i_name_len--;
948     }
949
950     if( i_name_len == 0 ) return;
951
952     psz_name = strndup( psz_option, i_name_len );
953     if( psz_value ) psz_value++;
954
955     /* FIXME: :programs should be handled generically */
956     if( !strcmp( psz_name, "programs" ) )
957         i_type = VLC_VAR_LIST;
958     else
959         i_type = config_GetType( p_obj, psz_name );
960
961     if( !i_type && !psz_value )
962     {
963         /* check for "no-foo" or "nofoo" */
964         if( !strncmp( psz_name, "no-", 3 ) )
965         {
966             memmove( psz_name, psz_name + 3, strlen(psz_name) + 1 - 3 );
967         }
968         else if( !strncmp( psz_name, "no", 2 ) )
969         {
970             memmove( psz_name, psz_name + 2, strlen(psz_name) + 1 - 2 );
971         }
972         else goto cleanup;           /* Option doesn't exist */
973
974         b_isno = VLC_TRUE;
975         i_type = config_GetType( p_obj, psz_name );
976
977         if( !i_type ) goto cleanup;  /* Option doesn't exist */
978     }
979     else if( !i_type ) goto cleanup; /* Option doesn't exist */
980
981     if( ( i_type != VLC_VAR_BOOL ) &&
982         ( !psz_value || !*psz_value ) ) goto cleanup; /* Invalid value */
983
984     /* Create the variable in the input object.
985      * Children of the input object will be able to retreive this value
986      * thanks to the inheritance property of the object variables. */
987     var_Create( p_obj, psz_name, i_type );
988
989     switch( i_type )
990     {
991     case VLC_VAR_BOOL:
992         val.b_bool = !b_isno;
993         break;
994
995     case VLC_VAR_INTEGER:
996         val.i_int = strtol( psz_value, NULL, 0 );
997         break;
998
999     case VLC_VAR_FLOAT:
1000         val.f_float = atof( psz_value );
1001         break;
1002
1003     case VLC_VAR_STRING:
1004     case VLC_VAR_MODULE:
1005     case VLC_VAR_FILE:
1006     case VLC_VAR_DIRECTORY:
1007         val.psz_string = psz_value;
1008         break;
1009
1010     case VLC_VAR_LIST:
1011     {
1012         char *psz_orig, *psz_var;
1013         vlc_list_t *p_list = malloc(sizeof(vlc_list_t));
1014         val.p_list = p_list;
1015         p_list->i_count = 0;
1016
1017         psz_var = psz_orig = strdup(psz_value);
1018         while( psz_var && *psz_var )
1019         {
1020             char *psz_item = psz_var;
1021             vlc_value_t val2;
1022             while( *psz_var && *psz_var != ',' ) psz_var++;
1023             if( *psz_var == ',' )
1024             {
1025                 *psz_var = '\0';
1026                 psz_var++;
1027             }
1028             val2.i_int = strtol( psz_item, NULL, 0 );
1029             INSERT_ELEM( p_list->p_values, p_list->i_count,
1030                          p_list->i_count, val2 );
1031             /* p_list->i_count is incremented twice by INSERT_ELEM */
1032             p_list->i_count--;
1033             INSERT_ELEM( p_list->pi_types, p_list->i_count,
1034                          p_list->i_count, VLC_VAR_INTEGER );
1035         }
1036         if( psz_orig ) free( psz_orig );
1037         break;
1038     }
1039
1040     default:
1041         goto cleanup;
1042         break;
1043     }
1044
1045     var_Set( p_obj, psz_name, val );
1046
1047   cleanup:
1048     if( psz_name ) free( psz_name );
1049     return;
1050 }
1051
1052
1053 /* Following functions are local */
1054
1055 /*****************************************************************************
1056  * GetUnused: find an unused variable from its name
1057  *****************************************************************************
1058  * We do i_tries tries before giving up, just in case the variable is being
1059  * modified and called from a callback.
1060  *****************************************************************************/
1061 static int GetUnused( vlc_object_t *p_this, const char *psz_name )
1062 {
1063     int i_var, i_tries = 0;
1064
1065     while( VLC_TRUE )
1066     {
1067         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
1068         if( i_var < 0 )
1069         {
1070             return VLC_ENOVAR;
1071         }
1072
1073         if( ! p_this->p_vars[i_var].b_incallback )
1074         {
1075             return i_var;
1076         }
1077
1078         if( i_tries++ > 100 )
1079         {
1080             msg_Err( p_this, "caught in a callback deadlock?" );
1081             return VLC_ETIMEOUT;
1082         }
1083
1084         vlc_mutex_unlock( &p_this->var_lock );
1085         msleep( THREAD_SLEEP );
1086         vlc_mutex_lock( &p_this->var_lock );
1087     }
1088 }
1089
1090 /*****************************************************************************
1091  * HashString: our cool hash function
1092  *****************************************************************************
1093  * This function is not intended to be crypto-secure, we only want it to be
1094  * fast and not suck too much. This one is pretty fast and did 0 collisions
1095  * in wenglish's dictionary.
1096  *****************************************************************************/
1097 static uint32_t HashString( const char *psz_string )
1098 {
1099     uint32_t i_hash = 0;
1100
1101     while( *psz_string )
1102     {
1103         i_hash += *psz_string++;
1104         i_hash += i_hash << 10;
1105         i_hash ^= i_hash >> 8;
1106     }
1107
1108     return i_hash;
1109 }
1110
1111 /*****************************************************************************
1112  * Insert: find an empty slot to insert a new variable
1113  *****************************************************************************
1114  * We use a recursive inner function indexed on the hash. This function does
1115  * nothing in the rare cases where a collision may occur, see Lookup()
1116  * to see how we handle them.
1117  * XXX: does this really need to be written recursively?
1118  *****************************************************************************/
1119 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
1120 {
1121     if( i_count == 0 )
1122     {
1123         return 0;
1124     }
1125
1126     return InsertInner( p_vars, i_count, HashString( psz_name ) );
1127 }
1128
1129 static int InsertInner( variable_t *p_vars, int i_count, uint32_t i_hash )
1130 {
1131     int i_middle;
1132
1133     if( i_hash <= p_vars[0].i_hash )
1134     {
1135         return 0;
1136     }
1137
1138     if( i_hash >= p_vars[i_count - 1].i_hash )
1139     {
1140         return i_count;
1141     }
1142
1143     i_middle = i_count / 2;
1144
1145     /* We know that 0 < i_middle */
1146     if( i_hash < p_vars[i_middle].i_hash )
1147     {
1148         return InsertInner( p_vars, i_middle, i_hash );
1149     }
1150
1151     /* We know that i_middle + 1 < i_count */
1152     if( i_hash > p_vars[i_middle + 1].i_hash )
1153     {
1154         return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
1155                                            i_count - i_middle - 1,
1156                                            i_hash );
1157     }
1158
1159     return i_middle + 1;
1160 }
1161
1162 /*****************************************************************************
1163  * Lookup: find an existing variable given its name
1164  *****************************************************************************
1165  * We use a recursive inner function indexed on the hash. Care is taken of
1166  * possible hash collisions.
1167  * XXX: does this really need to be written recursively?
1168  *****************************************************************************/
1169 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
1170 {
1171     uint32_t i_hash;
1172     int i, i_pos;
1173
1174     if( i_count == 0 )
1175     {
1176         return -1;
1177     }
1178
1179     i_hash = HashString( psz_name );
1180
1181     i_pos = LookupInner( p_vars, i_count, i_hash );
1182
1183     /* Hash not found */
1184     if( i_hash != p_vars[i_pos].i_hash )
1185     {
1186         return -1;
1187     }
1188
1189     /* Hash found, entry found */
1190     if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
1191     {
1192         return i_pos;
1193     }
1194
1195     /* Hash collision! This should be very rare, but we cannot guarantee
1196      * it will never happen. Just do an exhaustive search amongst all
1197      * entries with the same hash. */
1198     for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
1199     {
1200         if( !strcmp( psz_name, p_vars[i].psz_name ) )
1201         {
1202             return i;
1203         }
1204     }
1205
1206     for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
1207     {
1208         if( !strcmp( psz_name, p_vars[i].psz_name ) )
1209         {
1210             return i;
1211         }
1212     }
1213
1214     /* Hash found, but entry not found */
1215     return -1;
1216 }
1217
1218 static int LookupInner( variable_t *p_vars, int i_count, uint32_t i_hash )
1219 {
1220     int i_middle;
1221
1222     if( i_hash <= p_vars[0].i_hash )
1223     {
1224         return 0;
1225     }
1226
1227     if( i_hash >= p_vars[i_count-1].i_hash )
1228     {
1229         return i_count - 1;
1230     }
1231
1232     i_middle = i_count / 2;
1233
1234     /* We know that 0 < i_middle */
1235     if( i_hash < p_vars[i_middle].i_hash )
1236     {
1237         return LookupInner( p_vars, i_middle, i_hash );
1238     }
1239
1240     /* We know that i_middle + 1 < i_count */
1241     if( i_hash > p_vars[i_middle].i_hash )
1242     {
1243         return i_middle + LookupInner( p_vars + i_middle,
1244                                        i_count - i_middle,
1245                                        i_hash );
1246     }
1247
1248     return i_middle;
1249 }
1250
1251 /*****************************************************************************
1252  * CheckValue: check that a value is valid wrt. a variable
1253  *****************************************************************************
1254  * This function checks p_val's value against p_var's limitations such as
1255  * minimal and maximal value, step, in-list position, and modifies p_val if
1256  * necessary.
1257  ****************************************************************************/
1258 static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
1259 {
1260     /* Check that our variable is in the list */
1261     if( p_var->i_type & VLC_VAR_HASCHOICE && p_var->choices.i_count )
1262     {
1263         int i;
1264
1265         /* FIXME: the list is sorted, dude. Use something cleverer. */
1266         for( i = p_var->choices.i_count ; i-- ; )
1267         {
1268             if( p_var->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
1269             {
1270                 break;
1271             }
1272         }
1273
1274         /* If not found, change it to anything vaguely valid */
1275         if( i < 0 )
1276         {
1277             /* Free the old variable, get the new one, dup it */
1278             p_var->pf_free( p_val );
1279             *p_val = p_var->choices.p_values[p_var->i_default >= 0
1280                                           ? p_var->i_default : 0 ];
1281             p_var->pf_dup( p_val );
1282         }
1283     }
1284
1285     /* Check that our variable is within the bounds */
1286     switch( p_var->i_type & VLC_VAR_TYPE )
1287     {
1288         case VLC_VAR_INTEGER:
1289             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.i_int
1290                  && (p_val->i_int % p_var->step.i_int) )
1291             {
1292                 p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
1293                                / p_var->step.i_int * p_var->step.i_int;
1294             }
1295             if( p_var->i_type & VLC_VAR_HASMIN
1296                  && p_val->i_int < p_var->min.i_int )
1297             {
1298                 p_val->i_int = p_var->min.i_int;
1299             }
1300             if( p_var->i_type & VLC_VAR_HASMAX
1301                  && p_val->i_int > p_var->max.i_int )
1302             {
1303                 p_val->i_int = p_var->max.i_int;
1304             }
1305             break;
1306         case VLC_VAR_FLOAT:
1307             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.f_float )
1308             {
1309                 float f_round = p_var->step.f_float * (float)(int)( 0.5 +
1310                                         p_val->f_float / p_var->step.f_float );
1311                 if( p_val->f_float != f_round )
1312                 {
1313                     p_val->f_float = f_round;
1314                 }
1315             }
1316             if( p_var->i_type & VLC_VAR_HASMIN
1317                  && p_val->f_float < p_var->min.f_float )
1318             {
1319                 p_val->f_float = p_var->min.f_float;
1320             }
1321             if( p_var->i_type & VLC_VAR_HASMAX
1322                  && p_val->f_float > p_var->max.f_float )
1323             {
1324                 p_val->f_float = p_var->max.f_float;
1325             }
1326             break;
1327         case VLC_VAR_TIME:
1328             /* FIXME: TODO */
1329             break;
1330     }
1331 }
1332
1333 /*****************************************************************************
1334  * InheritValue: try to inherit the value of this variable from the same one
1335  *               in our closest parent.
1336  *****************************************************************************/
1337 static int InheritValue( vlc_object_t *p_this, const char *psz_name,
1338                          vlc_value_t *p_val, int i_type )
1339 {
1340     int i_var;
1341     variable_t *p_var;
1342
1343     /* No need to take the structure lock,
1344      * we are only looking for our parents */
1345
1346     if( !p_this->p_parent )
1347     {
1348         switch( i_type & VLC_VAR_TYPE )
1349         {
1350         case VLC_VAR_FILE:
1351         case VLC_VAR_DIRECTORY:
1352         case VLC_VAR_STRING:
1353         case VLC_VAR_MODULE:
1354             p_val->psz_string = config_GetPsz( p_this, psz_name );
1355             if( !p_val->psz_string ) p_val->psz_string = strdup("");
1356             break;
1357         case VLC_VAR_FLOAT:
1358             p_val->f_float = config_GetFloat( p_this, psz_name );
1359             break;
1360         case VLC_VAR_INTEGER:
1361         case VLC_VAR_HOTKEY:
1362             p_val->i_int = config_GetInt( p_this, psz_name );
1363             break;
1364         case VLC_VAR_BOOL:
1365             p_val->b_bool = config_GetInt( p_this, psz_name );
1366             break;
1367         case VLC_VAR_LIST:
1368         {
1369             char *psz_orig, *psz_var;
1370             vlc_list_t *p_list = malloc(sizeof(vlc_list_t));
1371             p_val->p_list = p_list;
1372             p_list->i_count = 0;
1373
1374             psz_var = psz_orig = config_GetPsz( p_this, psz_name );
1375             while( psz_var && *psz_var )
1376             {
1377                 char *psz_item = psz_var;
1378                 vlc_value_t val;
1379                 while( *psz_var && *psz_var != ',' ) psz_var++;
1380                 if( *psz_var == ',' )
1381                 {
1382                     *psz_var = '\0';
1383                     psz_var++;
1384                 }
1385                 val.i_int = strtol( psz_item, NULL, 0 );
1386                 INSERT_ELEM( p_list->p_values, p_list->i_count,
1387                              p_list->i_count, val );
1388                 /* p_list->i_count is incremented twice by INSERT_ELEM */
1389                 p_list->i_count--;
1390                 INSERT_ELEM( p_list->pi_types, p_list->i_count,
1391                              p_list->i_count, VLC_VAR_INTEGER );
1392             }
1393             if( psz_orig ) free( psz_orig );
1394             break;
1395         }
1396         default:
1397             return VLC_ENOOBJ;
1398             break;
1399         }
1400
1401         return VLC_SUCCESS;
1402     }
1403
1404     /* Look for the variable */
1405     vlc_mutex_lock( &p_this->p_parent->var_lock );
1406
1407     i_var = Lookup( p_this->p_parent->p_vars, p_this->p_parent->i_vars,
1408                     psz_name );
1409
1410     if( i_var >= 0 )
1411     {
1412         /* We found it! */
1413         p_var = &p_this->p_parent->p_vars[i_var];
1414
1415         /* Really get the variable */
1416         *p_val = p_var->val;
1417
1418         /* Duplicate value if needed */
1419         p_var->pf_dup( p_val );
1420
1421         vlc_mutex_unlock( &p_this->p_parent->var_lock );
1422         return VLC_SUCCESS;
1423     }
1424
1425     vlc_mutex_unlock( &p_this->p_parent->var_lock );
1426
1427     /* We're still not there */
1428
1429     return InheritValue( p_this->p_parent, psz_name, p_val, i_type );
1430 }