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