]> git.sesse.net Git - vlc/blobdiff - src/misc/variables.c
* ALL: experimental code for stream (dvd) navigation through object variables.
[vlc] / src / misc / variables.c
index 3db0d7e5fc541b8f7b505869402d0abace7bb9aa..72a59ca0946e5e3cebff3ab8c9e214986d5acd13 100644 (file)
@@ -2,7 +2,7 @@
  * variables.c: routines for object variables handling
  *****************************************************************************
  * Copyright (C) 2002 VideoLAN
- * $Id: variables.c,v 1.6 2002/10/16 10:31:58 sam Exp $
+ * $Id: variables.c,v 1.21 2003/03/11 23:56:54 gbazin Exp $
  *
  * Authors: Samuel Hocevar <sam@zoy.org>
  *
@@ -10,7 +10,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #   include <stdlib.h>                                          /* realloc() */
 #endif
 
+/*****************************************************************************
+ * Private types
+ *****************************************************************************/
+struct callback_entry_t
+{
+    vlc_callback_t pf_callback;
+    void *         p_data;
+};
+
+/*****************************************************************************
+ * Local comparison functions, returns 0 if v == w, < 0 if v < w, > 0 if v > w
+ *****************************************************************************/
+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; }
+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; }
+static int CmpString( vlc_value_t v, vlc_value_t w ) { return strcmp( v.psz_string, w.psz_string ); }
+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; }
+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; }
+
+/*****************************************************************************
+ * Local duplication functions, and local deallocation functions
+ *****************************************************************************/
+static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
+static void DupString( vlc_value_t *p_val ) { p_val->psz_string = strdup( p_val->psz_string ); }
+
+static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
+static void FreeString( vlc_value_t *p_val ) { free( p_val->psz_string ); }
+static void FreeMutex( vlc_value_t *p_val ) { vlc_mutex_destroy( (vlc_mutex_t*)p_val->p_address ); free( p_val->p_address ); }
+
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static u32 HashString     ( const char * );
-static int Insert         ( variable_t *, int, const char * );
-static int InsertInner    ( variable_t *, int, u32 );
-static int Lookup         ( variable_t *, int, const char * );
-static int LookupInner    ( variable_t *, int, u32 );
+static int      GetUnused   ( vlc_object_t *, const char * );
+static uint32_t HashString  ( const char * );
+static int      Insert      ( variable_t *, int, const char * );
+static int      InsertInner ( variable_t *, int, uint32_t );
+static int      Lookup      ( variable_t *, int, const char * );
+static int      LookupInner ( variable_t *, int, uint32_t );
+
+static void     CheckValue  ( variable_t *, vlc_value_t * );
 
 /*****************************************************************************
  * var_Create: initialize a vlc variable
@@ -95,19 +126,64 @@ int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
     p_var->i_type = i_type;
     memset( &p_var->val, 0, sizeof(vlc_value_t) );
 
+    p_var->pf_dup = DupDummy;
+    p_var->pf_free = FreeDummy;
+
     p_var->i_usage = 1;
-    p_var->b_set = VLC_FALSE;
-    p_var->b_active = VLC_TRUE;
 
-    switch( i_type )
+    p_var->i_default = -1;
+    p_var->choices.i_count = 0;
+    p_var->choices.p_values = NULL;
+
+    p_var->b_incallback = VLC_FALSE;
+    p_var->i_entries = 0;
+    p_var->p_entries = NULL;
+
+    /* Always initialize the variable, even if it is a list variable; this
+     * will lead to errors if the variable is not initialized, but it will
+     * not cause crashes in the variable handling. */
+    switch( i_type & VLC_VAR_TYPE )
     {
+        case VLC_VAR_BOOL:
+            p_var->pf_cmp = CmpBool;
+            p_var->val.b_bool = VLC_FALSE;
+            break;
+        case VLC_VAR_INTEGER:
+            p_var->pf_cmp = CmpInt;
+            p_var->val.i_int = 0;
+            break;
+        case VLC_VAR_STRING:
+        case VLC_VAR_MODULE:
+        case VLC_VAR_FILE:
+        case VLC_VAR_DIRECTORY:
+        case VLC_VAR_VARIABLE:
+            p_var->pf_cmp = CmpString;
+            p_var->pf_dup = DupString;
+            p_var->pf_free = FreeString;
+            p_var->val.psz_string = "";
+            break;
+        case VLC_VAR_FLOAT:
+            p_var->pf_cmp = CmpFloat;
+            p_var->val.f_float = 0.0;
+            break;
+        case VLC_VAR_TIME:
+            /* FIXME: TODO */
+            break;
+        case VLC_VAR_ADDRESS:
+            p_var->pf_cmp = CmpAddress;
+            p_var->val.p_address = NULL;
+            break;
         case VLC_VAR_MUTEX:
+            p_var->pf_cmp = CmpAddress;
+            p_var->pf_free = FreeMutex;
             p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
             vlc_mutex_init( p_this, (vlc_mutex_t*)p_var->val.p_address );
-            p_var->b_set = VLC_TRUE;
             break;
     }
 
+    /* Duplicate the default data we stored. */
+    p_var->pf_dup( &p_var->val );
+
     vlc_mutex_unlock( &p_this->var_lock );
 
     return VLC_SUCCESS;
@@ -121,20 +197,19 @@ int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
  *****************************************************************************/
 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
 {
-    int i_del;
+    int i_var, i;
     variable_t *p_var;
 
     vlc_mutex_lock( &p_this->var_lock );
 
-    i_del = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
-
-    if( i_del < 0 )
+    i_var = GetUnused( p_this, psz_name );
+    if( i_var < 0 )
     {
         vlc_mutex_unlock( &p_this->var_lock );
-        return VLC_ENOVAR;
+        return i_var;
     }
 
-    p_var = &p_this->p_vars[i_del];
+    p_var = &p_this->p_vars[i_var];
 
     if( p_var->i_usage > 1 )
     {
@@ -144,28 +219,29 @@ int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
     }
 
     /* Free value if needed */
-    switch( p_var->i_type )
+    p_var->pf_free( &p_var->val );
+
+    /* Free choice list if needed */
+    if( p_var->choices.i_count )
     {
-        case VLC_VAR_STRING:
-        case VLC_VAR_MODULE:
-        case VLC_VAR_FILE:
-            if( p_var->b_set && p_var->val.psz_string )
-            {
-                free( p_var->val.psz_string );
-            }
-            break;
+        for( i = 0 ; i < p_var->choices.i_count ; i++ )
+        {
+            p_var->pf_free( &p_var->choices.p_values[i] );
+        }
+        free( p_var->choices.p_values );
+    }
 
-        case VLC_VAR_MUTEX:
-            vlc_mutex_destroy( (vlc_mutex_t*)p_var->val.p_address );
-            free( p_var->val.p_address );
-            break;
+    /* Free callbacks if needed */
+    if( p_var->p_entries )
+    {
+        free( p_var->p_entries );
     }
 
     free( p_var->psz_name );
 
-    memmove( p_this->p_vars + i_del,
-             p_this->p_vars + i_del + 1,
-             (p_this->i_vars - i_del - 1) * sizeof(variable_t) );
+    memmove( p_this->p_vars + i_var,
+             p_this->p_vars + i_var + 1,
+             (p_this->i_vars - i_var - 1) * sizeof(variable_t) );
 
     if( (p_this->i_vars & 15) == 0 )
     {
@@ -181,9 +257,195 @@ int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
 }
 
 /*****************************************************************************
- * var_Type: request a variable's type, 0 if not found
+ * var_Change: perform an action on a variable
  *****************************************************************************
- * 
+ *
+ *****************************************************************************/
+int __var_Change( vlc_object_t *p_this, const char *psz_name,
+                  int i_action, vlc_value_t *p_val )
+{
+    int i_var, i;
+    variable_t *p_var;
+    vlc_value_t oldval;
+
+    vlc_mutex_lock( &p_this->var_lock );
+
+    i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
+
+    if( i_var < 0 )
+    {
+        vlc_mutex_unlock( &p_this->var_lock );
+        return VLC_ENOVAR;
+    }
+
+    p_var = &p_this->p_vars[i_var];
+
+    switch( i_action )
+    {
+        case VLC_VAR_SETMIN:
+            if( p_var->i_type & VLC_VAR_HASMIN )
+            {
+                p_var->pf_free( &p_var->min );
+            }
+            p_var->i_type |= VLC_VAR_HASMIN;
+            p_var->min = *p_val;
+            p_var->pf_dup( &p_var->min );
+            CheckValue( p_var, &p_var->val );
+            break;
+        case VLC_VAR_SETMAX:
+            if( p_var->i_type & VLC_VAR_HASMAX )
+            {
+                p_var->pf_free( &p_var->max );
+            }
+            p_var->i_type |= VLC_VAR_HASMAX;
+            p_var->max = *p_val;
+            p_var->pf_dup( &p_var->max );
+            CheckValue( p_var, &p_var->val );
+            break;
+        case VLC_VAR_SETSTEP:
+            if( p_var->i_type & VLC_VAR_HASSTEP )
+            {
+                p_var->pf_free( &p_var->step );
+            }
+            p_var->i_type |= VLC_VAR_HASSTEP;
+            p_var->step = *p_val;
+            p_var->pf_dup( &p_var->step );
+            CheckValue( p_var, &p_var->val );
+            break;
+        case VLC_VAR_ADDCHOICE:
+            /* FIXME: the list is sorted, dude. Use something cleverer. */
+            for( i = p_var->choices.i_count ; i-- ; )
+            {
+                if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) < 0 )
+                {
+                    break;
+                }
+            }
+
+            /* The new place is i+1 */
+            i++;
+
+            if( p_var->i_default >= i )
+            {
+                p_var->i_default++;
+            }
+
+            INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
+                         i, *p_val );
+            p_var->pf_dup( &p_var->choices.p_values[i] );
+
+            CheckValue( p_var, &p_var->val );
+            break;
+        case VLC_VAR_DELCHOICE:
+            /* FIXME: the list is sorted, dude. Use something cleverer. */
+            for( i = 0 ; i < p_var->choices.i_count ; i++ )
+            {
+                if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
+                {
+                    break;
+                }
+            }
+
+            if( i == p_var->choices.i_count )
+            {
+                /* Not found */
+                vlc_mutex_unlock( &p_this->var_lock );
+                return VLC_EGENERIC;
+            }
+
+            if( p_var->i_default > i )
+            {
+                p_var->i_default--;
+            }
+            else if( p_var->i_default == i )
+            {
+                p_var->i_default = -1;
+            }
+
+            p_var->pf_free( &p_var->choices.p_values[i] );
+            REMOVE_ELEM( p_var->choices.p_values, p_var->choices.i_count, i );
+
+            CheckValue( p_var, &p_var->val );
+            break;
+        case VLC_VAR_CLEARCHOICES:
+            for( i = 0 ; i < p_var->choices.i_count ; i++ )
+            {
+                p_var->pf_free( &p_var->choices.p_values[i] );
+            }
+            if( p_var->choices.i_count )
+                free( p_var->choices.p_values );
+
+            p_var->choices.i_count = 0;
+            p_var->choices.p_values = NULL;
+            p_var->i_default = -1;
+            break;
+        case VLC_VAR_SETDEFAULT:
+            /* FIXME: the list is sorted, dude. Use something cleverer. */
+            for( i = 0 ; i < p_var->choices.i_count ; i++ )
+            {
+                if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
+                {
+                    break;
+                }
+            }
+
+            if( i == p_var->choices.i_count )
+            {
+                /* Not found */
+                break;
+            }
+
+            p_var->i_default = i;
+            CheckValue( p_var, &p_var->val );
+            break;
+        case VLC_VAR_SETVALUE:
+            /* Duplicate data if needed */
+            p_var->pf_dup( p_val );
+            /* Backup needed stuff */
+            oldval = p_var->val;
+            /* Check boundaries and list */
+            CheckValue( p_var, p_val );
+            /* Set the variable */
+            p_var->val = *p_val;
+            /* Free data if needed */
+            p_var->pf_free( &oldval );
+            break;
+        case VLC_VAR_GETLIST:
+            p_val->p_list = malloc( sizeof(vlc_list_t) );
+            if( p_var->choices.i_count )
+                p_val->p_list->p_values = malloc( p_var->choices.i_count
+                                                  * sizeof(vlc_value_t) );
+            p_val->p_list->i_count = p_var->choices.i_count;
+            for( i = 0 ; i < p_var->choices.i_count ; i++ )
+            {
+                p_val->p_list->p_values[i] = p_var->choices.p_values[i];
+                p_var->pf_dup( &p_val->p_list->p_values[i] );
+            }
+            break;
+        case VLC_VAR_FREELIST:
+            for( i = p_val->p_list->i_count ; i-- ; )
+            {
+                p_var->pf_free( &p_val->p_list->p_values[i] );
+            }
+            if( p_val->p_list->i_count )
+                free( p_val->p_list->p_values );
+            free( p_val->p_list );
+            break;
+
+        default:
+            break;
+    }
+
+    vlc_mutex_unlock( &p_this->var_lock );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * var_Type: request a variable's type
+ *****************************************************************************
+ * This function returns the variable type if it exists, or 0 if the
+ * variable could not be found.
  *****************************************************************************/
 int __var_Type( vlc_object_t *p_this, const char *psz_name )
 {
@@ -214,39 +476,66 @@ int __var_Type( vlc_object_t *p_this, const char *psz_name )
 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
 {
     int i_var;
+    variable_t *p_var;
+    vlc_value_t oldval;
 
     vlc_mutex_lock( &p_this->var_lock );
 
-    i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
-
+    i_var = GetUnused( p_this, psz_name );
     if( i_var < 0 )
     {
         vlc_mutex_unlock( &p_this->var_lock );
-        return VLC_ENOVAR;
+        return i_var;
     }
 
-    /* Duplicate value if needed */
-    switch( p_this->p_vars[i_var].i_type )
+    p_var = &p_this->p_vars[i_var];
+
+    /* Duplicate data if needed */
+    p_var->pf_dup( &val );
+
+    /* Backup needed stuff */
+    oldval = p_var->val;
+
+    /* Check boundaries and list */
+    CheckValue( p_var, &val );
+
+    /* Set the variable */
+    p_var->val = val;
+
+    /* Deal with callbacks. Tell we're in a callback, release the lock,
+     * call stored functions, retake the lock. */
+    if( p_var->i_entries )
     {
-        case VLC_VAR_STRING:
-        case VLC_VAR_MODULE:
-        case VLC_VAR_FILE:
-            if( p_this->p_vars[i_var].b_set
-                 && p_this->p_vars[i_var].val.psz_string )
-            {
-                free( p_this->p_vars[i_var].val.psz_string );
-            }
-            if( val.psz_string )
-            {
-                val.psz_string = strdup( val.psz_string );
-            }
-            break;
-    }
+        int i_var;
+        int i_entries = p_var->i_entries;
+        callback_entry_t *p_entries = p_var->p_entries;
+
+        p_var->b_incallback = VLC_TRUE;
+        vlc_mutex_unlock( &p_this->var_lock );
+
+        /* The real calls */
+        for( ; i_entries-- ; )
+        {
+            p_entries[i_entries].pf_callback( p_this, psz_name, oldval, val,
+                                              p_entries[i_entries].p_data );
+        }
+
+        vlc_mutex_lock( &p_this->var_lock );
 
-    p_this->p_vars[i_var].val = val;
-    p_this->p_vars[i_var].b_set = VLC_TRUE;
+        i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
+        if( i_var < 0 )
+        {
+            msg_Err( p_this, "variable %s has disappeared", psz_name );
+            vlc_mutex_unlock( &p_this->var_lock );
+            return VLC_ENOVAR;
+        }
+
+        p_var = &p_this->p_vars[i_var];
+        p_var->b_incallback = VLC_FALSE;
+    }
 
-    /* XXX: callback stuff will go here */
+    /* Free data if needed */
+    p_var->pf_free( &oldval );
 
     vlc_mutex_unlock( &p_this->var_lock );
 
@@ -261,6 +550,7 @@ int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
 {
     int i_var;
+    variable_t *p_var;
 
     vlc_mutex_lock( &p_this->var_lock );
 
@@ -272,51 +562,97 @@ int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
         return VLC_ENOVAR;
     }
 
-    if( !p_this->p_vars[i_var].b_set )
+    p_var = &p_this->p_vars[i_var];
+
+    /* Really get the variable */
+    *p_val = p_var->val;
+
+    /* Duplicate value if needed */
+    p_var->pf_dup( p_val );
+
+    vlc_mutex_unlock( &p_this->var_lock );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * var_AddCallback: register a callback in a variable
+ *****************************************************************************
+ * We store a function pointer pf_callback that will be called upon variable
+ * modification. p_data is a generic pointer that will be passed as additional
+ * argument to the callback function.
+ *****************************************************************************/
+int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
+                       vlc_callback_t pf_callback, void *p_data )
+{
+    int i_var;
+    variable_t *p_var;
+    callback_entry_t entry;
+
+    entry.pf_callback = pf_callback;
+    entry.p_data = p_data;
+
+    vlc_mutex_lock( &p_this->var_lock );
+
+    i_var = GetUnused( p_this, psz_name );
+    if( i_var < 0 )
     {
         vlc_mutex_unlock( &p_this->var_lock );
-        return VLC_EBADVAR;
+        return i_var;
     }
 
-    /* Some variables trigger special behaviour. */
-    switch( p_this->p_vars[i_var].i_type )
-    {
-        case VLC_VAR_COMMAND:
-            if( p_this->p_vars[i_var].b_set )
-            {
-                /* We need this to avoid deadlocks */
-                int i_ret;
-                int (*pf_command) (vlc_object_t *, char *, char *) =
-                                          p_this->p_vars[i_var].val.p_address;
-                char *psz_cmd = strdup( p_this->p_vars[i_var].psz_name );
-                char *psz_arg = strdup( p_val->psz_string );
+    p_var = &p_this->p_vars[i_var];
 
-                vlc_mutex_unlock( &p_this->var_lock );
-                i_ret = pf_command( p_this, psz_cmd, psz_arg );
-                free( psz_cmd );
-                free( psz_arg );
+    INSERT_ELEM( p_var->p_entries,
+                 p_var->i_entries,
+                 p_var->i_entries,
+                 entry );
 
-                return i_ret;
-            }
-            break;
+    vlc_mutex_unlock( &p_this->var_lock );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * var_DelCallback: remove a callback from a variable
+ *****************************************************************************
+ * pf_callback and p_data have to be given again, because different objects
+ * might have registered the same callback function.
+ *****************************************************************************/
+int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
+                       vlc_callback_t pf_callback, void *p_data )
+{
+    int i_entry, i_var;
+    variable_t *p_var;
+
+    vlc_mutex_lock( &p_this->var_lock );
+
+    i_var = GetUnused( p_this, psz_name );
+    if( i_var < 0 )
+    {
+        vlc_mutex_unlock( &p_this->var_lock );
+        return i_var;
     }
 
-    /* Really set the variable */
-    *p_val = p_this->p_vars[i_var].val;
+    p_var = &p_this->p_vars[i_var];
 
-    /* Duplicate value if needed */
-    switch( p_this->p_vars[i_var].i_type )
+    for( i_entry = p_var->i_entries ; i_entry-- ; )
     {
-        case VLC_VAR_STRING:
-        case VLC_VAR_MODULE:
-        case VLC_VAR_FILE:
-            if( p_val->psz_string )
-            {
-                p_val->psz_string = strdup( p_val->psz_string );
-            }
+        if( p_var->p_entries[i_entry].pf_callback == pf_callback
+            && p_var->p_entries[i_entry].p_data == p_data )
+        {
             break;
+        }
+    }
+
+    if( i_entry < 0 )
+    {
+        vlc_mutex_unlock( &p_this->var_lock );
+        return VLC_EGENERIC;
     }
 
+    REMOVE_ELEM( p_var->p_entries, p_var->i_entries, i_entry );
+
     vlc_mutex_unlock( &p_this->var_lock );
 
     return VLC_SUCCESS;
@@ -324,6 +660,41 @@ int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
 
 /* Following functions are local */
 
+/*****************************************************************************
+ * GetUnused: find an unused variable from its name
+ *****************************************************************************
+ * We do i_tries tries before giving up, just in case the variable is being
+ * modified and called from a callback.
+ *****************************************************************************/
+static int GetUnused( vlc_object_t *p_this, const char *psz_name )
+{
+    int i_var, i_tries = 0;
+
+    while( VLC_TRUE )
+    {
+        i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
+        if( i_var < 0 )
+        {
+            return VLC_ENOVAR;
+        }
+
+        if( ! p_this->p_vars[i_var].b_incallback )
+        {
+            return i_var;
+        }
+
+        if( i_tries++ > 100 )
+        {
+            msg_Err( p_this, "caught in a callback deadlock?" );
+            return VLC_ETIMEOUT;
+        }
+
+        vlc_mutex_unlock( &p_this->var_lock );
+        msleep( THREAD_SLEEP );
+        vlc_mutex_lock( &p_this->var_lock );
+    }
+}
+
 /*****************************************************************************
  * HashString: our cool hash function
  *****************************************************************************
@@ -331,9 +702,9 @@ int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
  * fast and not suck too much. This one is pretty fast and did 0 collisions
  * in wenglish's dictionary.
  *****************************************************************************/
-static u32 HashString( const char *psz_string )
+static uint32_t HashString( const char *psz_string )
 {
-    u32 i_hash = 0;
+    uint32_t i_hash = 0;
 
     while( *psz_string )
     {
@@ -363,7 +734,7 @@ static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
     return InsertInner( p_vars, i_count, HashString( psz_name ) );
 }
 
-static int InsertInner( variable_t *p_vars, int i_count, u32 i_hash )
+static int InsertInner( variable_t *p_vars, int i_count, uint32_t i_hash )
 {
     int i_middle;
 
@@ -405,7 +776,7 @@ static int InsertInner( variable_t *p_vars, int i_count, u32 i_hash )
  *****************************************************************************/
 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
 {
-    u32 i_hash;
+    uint32_t i_hash;
     int i, i_pos;
 
     if( i_count == 0 )
@@ -452,7 +823,7 @@ static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
     return -1;
 }
 
-static int LookupInner( variable_t *p_vars, int i_count, u32 i_hash )
+static int LookupInner( variable_t *p_vars, int i_count, uint32_t i_hash )
 {
     int i_middle;
 
@@ -485,3 +856,84 @@ static int LookupInner( variable_t *p_vars, int i_count, u32 i_hash )
     return i_middle;
 }
 
+/*****************************************************************************
+ * CheckValue: check that a value is valid wrt. a variable
+ *****************************************************************************
+ * This function checks p_val's value against p_var's limitations such as
+ * minimal and maximal value, step, in-list position, and modifies p_val if
+ * necessary.
+ *****************************************************************************/
+static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
+{
+    /* Check that our variable is in the list */
+    if( p_var->i_type & VLC_VAR_HASCHOICE && p_var->choices.i_count )
+    {
+        int i;
+
+        /* FIXME: the list is sorted, dude. Use something cleverer. */
+        for( i = p_var->choices.i_count ; i-- ; )
+        {
+            if( p_var->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
+            {
+                break;
+            }
+        }
+
+        /* If not found, change it to anything vaguely valid */
+        if( i < 0 )
+        {
+            /* Free the old variable, get the new one, dup it */
+            p_var->pf_free( p_val );
+            *p_val = p_var->choices.p_values[p_var->i_default >= 0
+                                          ? p_var->i_default : 0 ];
+            p_var->pf_dup( p_val );
+        }
+    }
+
+    /* Check that our variable is within the bounds */
+    switch( p_var->i_type & VLC_VAR_TYPE )
+    {
+        case VLC_VAR_INTEGER:
+            if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.i_int
+                 && (p_val->i_int % p_var->step.i_int) )
+            {
+                p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
+                               / p_var->step.i_int * p_var->step.i_int;
+            }
+            if( p_var->i_type & VLC_VAR_HASMIN
+                 && p_val->i_int < p_var->min.i_int )
+            {
+                p_val->i_int = p_var->min.i_int;
+            }
+            if( p_var->i_type & VLC_VAR_HASMAX
+                 && p_val->i_int > p_var->max.i_int )
+            {
+                p_val->i_int = p_var->max.i_int;
+            }
+            break;
+        case VLC_VAR_FLOAT:
+            if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.f_float )
+            {
+                float f_round = p_var->step.f_float * (float)(int)( 0.5 +
+                                        p_val->f_float / p_var->step.f_float );
+                if( p_val->f_float != f_round )
+                {
+                    p_val->f_float = f_round;
+                }
+            }
+            if( p_var->i_type & VLC_VAR_HASMIN
+                 && p_val->f_float < p_var->min.f_float )
+            {
+                p_val->f_float = p_var->min.f_float;
+            }
+            if( p_var->i_type & VLC_VAR_HASMAX
+                 && p_val->f_float > p_var->max.f_float )
+            {
+                p_val->f_float = p_var->max.f_float;
+            }
+            break;
+        case VLC_VAR_TIME:
+            /* FIXME: TODO */
+            break;
+    }
+}