Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/src/misc/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 32 kB image not shown  

Quelle  variables.c   Sprache: C

 

/*****************************************************************************
 * variables.c: routines for object variables handling
 *****************************************************************************
 * Copyright (C) 2002-2009 VLC authors and VideoLAN
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef HAVE_SEARCH_H
# include <search.h>
#endif
#include <assert.h>
#include <float.h>
#include <math.h>
#include <limits.h>

#include <vlc_common.h>
#include <vlc_arrays.h>
#include <vlc_charset.h>
#include "../libvlc.h"
#include "variables.h"
#include "config/configuration.h"

typedef struct callback_entry_t
{
    struct callback_entry_t *next;
    union
    {
        vlc_callback_t       pf_value_callback;
        vlc_list_callback_t  pf_list_callback;
        void *               p_callback;
    };
    void *         p_data;
} callback_entry_t;

typedef struct variable_ops_t
{
    int  (*pf_cmp) ( vlc_value_t, vlc_value_t );
    void (*pf_dup) ( vlc_value_t * );
    void (*pf_free) ( vlc_value_t * );
} variable_ops_t;

/**
 * The structure describing a variable.
 * \note vlc_value_t is the common union for variable values
 */

struct variable_t
{
    char *       psz_name; /**< The variable unique name (must be first) */

    /** The variable's exported value */
    vlc_value_t  val;

    /** The variable display name, mainly for use by the interfaces */
    char *       psz_text;

    const variable_ops_t *ops;

    int          i_type;   /**< The type of the variable */
    unsigned     i_usage;  /**< Reference count */

    /** If the variable has min/max/step values */
    vlc_value_t  min, max, step;

    /** List of choices */
    vlc_value_t *choices;
    /** List of friendly names for the choices */
    char       **choices_text;
    size_t       choices_count;

    /** Set to TRUE if the variable is in a callback */
    bool   b_incallback;

    /** Registered value callbacks */
    callback_entry_t    *value_callbacks;
    /** Registered list callbacks */
    callback_entry_t    *list_callbacks;

    vlc_cond_t   wait;
};

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 )
{
    if( !v.psz_string )
        return !w.psz_string ? 0 : -1;
    else
        return !w.psz_string ? 1 : 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; }

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 ? 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 const struct variable_ops_t
void_ops   = { NULL,       DupDummy,  FreeDummy,  },
addr_ops   = { CmpAddress, DupDummy,  FreeDummy,  },
bool_ops   = { CmpBool,    DupDummy,  FreeDummy,  },
float_ops  = { CmpFloat,   DupDummy,  FreeDummy,  },
int_ops    = { CmpInt,     DupDummy,  FreeDummy,  },
string_ops = { CmpString,  DupString, FreeString, },
coords_ops = { NULL,       DupDummy,  FreeDummy,  };

static int varcmp( const void *a, const void *b )
{
    const variable_t *va = a, *vb = b;

    /* psz_name must be first */
    assert( va == (const void *)&va->psz_name );
    return strcmp( va->psz_name, vb->psz_name );
}

static variable_t *Lookup( vlc_object_t *obj, const char *psz_name )
{
    vlc_object_internals_t *priv = vlc_internals( obj );
    void **pp_var;

    vlc_mutex_lock(&priv->var_lock);
    pp_var = tfind( &psz_name, &priv->var_root, varcmp );
    return (pp_var != NULL) ? *pp_var : NULL;
}

static void Destroy( variable_t *p_var )
{
    p_var->ops->pf_free( &p_var->val );

    for (size_t i = 0, count = p_var->choices_count; i < count; i++)
    {
        p_var->ops->pf_free(&p_var->choices[i]);
        free(p_var->choices_text[i]);
    }
    free(p_var->choices);
    free(p_var->choices_text);

    free( p_var->psz_name );
    free( p_var->psz_text );
    while (unlikely(p_var->value_callbacks != NULL))
    {
        callback_entry_t *next = p_var->value_callbacks->next;

        free(p_var->value_callbacks);
        p_var->value_callbacks = next;
    }
    assert(p_var->list_callbacks == NULL);
    free( p_var );
}

/**
 * Adjusts a value to fit the constraints for a certain variable:
 * - If the value is lower than the minimum, use the minimum.
 * - If the value is higher than the maximum, use the maximum.
 * - If the variable has steps, round the value to the nearest step.
 */

static void CheckValue(variable_t *var, vlc_value_t *val)
{
    /* Check that our variable is within the bounds */
    switch (var->i_type & VLC_VAR_TYPE)
    {
        case VLC_VAR_INTEGER:
            if (val->i_int < var->min.i_int)
               val->i_int = var->min.i_int;
            else if (val->i_int > var->max.i_int)
                val->i_int = var->max.i_int;
            if (var->step.i_int != 0 && (val->i_int % var->step.i_int))
            {
                if (val->i_int > 0)
                    val->i_int = (val->i_int + (var->step.i_int / 2))
                                 / var->step.i_int * var->step.i_int;
                else
                    val->i_int = (val->i_int - (var->step.i_int / 2))
                                 / var->step.i_int * var->step.i_int;
            }
            break;

        case VLC_VAR_FLOAT:
            if (isless(val->f_float, var->min.f_float))
                val->f_float = var->min.f_float;
            else if (isgreater(val->f_float, var->max.f_float))
                val->f_float = var->max.f_float;
            if (var->step.f_float != 0.f)
                val->f_float = var->step.f_float
                              * roundf(val->f_float / var->step.f_float);
            break;
    }
}

/**
 * Waits until the variable is inactive (i.e. not executing a callback)
 */

static void WaitUnused(vlc_object_t *obj, variable_t *var)
{
    vlc_object_internals_t *priv = vlc_internals(obj);

    while (var->b_incallback)
        vlc_cond_wait(&var->wait, &priv->var_lock);
}

static void TriggerCallback(vlc_object_t *obj, variable_t *var,
                            const char *name, vlc_value_t prev)
{
    assert(obj != NULL);

    callback_entry_t *entry = var->value_callbacks;
    if (entry == NULL)
        return;

    vlc_object_internals_t *priv = vlc_internals(obj);

    assert(!var->b_incallback);
    var->b_incallback = true;
    vlc_mutex_unlock(&priv->var_lock);

    do
    {
        entry->pf_value_callback(obj, name, prev, var->val, entry->p_data);
        entry = entry->next;
    }
    while (entry != NULL);

    vlc_mutex_lock(&priv->var_lock);
    var->b_incallback = false;
    vlc_cond_broadcast(&var->wait);
}

static void TriggerListCallback(vlc_object_t *obj, variable_t *var,
                                const char *name, int action, vlc_value_t *val)
{
    assert(obj != NULL);

    callback_entry_t *entry = var->list_callbacks;
    if (entry == NULL)
        return;

    vlc_object_internals_t *priv = vlc_internals(obj);

    assert(!var->b_incallback);
    var->b_incallback = true;
    vlc_mutex_unlock(&priv->var_lock);

    do
    {
        entry->pf_list_callback(obj, name, action, val, entry->p_data);
        entry = entry->next;
    }
    while (entry != NULL);

    vlc_mutex_lock(&priv->var_lock);
    var->b_incallback = false;
    vlc_cond_broadcast(&var->wait);
}

int (var_Create)( vlc_object_t *p_this, const char *psz_name, int i_type )
{
    assert( p_this );

    variable_t *p_var = calloc( 1, sizeof( *p_var ) );
    if( p_var == NULL )
        return VLC_ENOMEM;

    p_var->psz_name = strdup( psz_name );
    p_var->psz_text = NULL;

    p_var->i_type = i_type & ~VLC_VAR_DOINHERIT;

    p_var->i_usage = 1;

    p_var->choices_count = 0;
    p_var->choices = NULL;
    p_var->choices_text = NULL;

    p_var->b_incallback = false;
    p_var->value_callbacks = 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_CLASS )
    {
        case VLC_VAR_BOOL:
            p_var->ops = &bool_ops;
            p_var->val.b_bool = false;
            break;
        case VLC_VAR_INTEGER:
            p_var->ops = &int_ops;
            p_var->val.i_int = 0;
            p_var->min.i_int = INT64_MIN;
            p_var->max.i_int = INT64_MAX;
            break;
        case VLC_VAR_STRING:
            p_var->ops = &string_ops;
            p_var->val.psz_string = NULL;
            break;
        case VLC_VAR_FLOAT:
            p_var->ops = &float_ops;
            p_var->val.f_float = 0.f;
            p_var->min.f_float = -FLT_MAX;
            p_var->max.f_float = FLT_MAX;
            break;
        case VLC_VAR_COORDS:
            p_var->ops = &coords_ops;
            p_var->val.coords.x = p_var->val.coords.y = 0;
            break;
        case VLC_VAR_ADDRESS:
            p_var->ops = &addr_ops;
            p_var->val.p_address = NULL;
            break;
        case VLC_VAR_VOID:
            p_var->ops = &void_ops;
            break;
        default:
            vlc_assert_unreachable ();
    }

    vlc_cond_init(&p_var->wait);

    if (i_type & VLC_VAR_DOINHERIT)
        var_Inherit(p_this, psz_name, i_type, &p_var->val);

    vlc_object_internals_t *p_priv = vlc_internals( p_this );
    void **pp_var;
    variable_t *p_oldvar;
    int ret = VLC_SUCCESS;

    vlc_mutex_lock( &p_priv->var_lock );

    pp_var = tsearch( p_var, &p_priv->var_root, varcmp );
    if( unlikely(pp_var == NULL) )
        ret = VLC_ENOMEM;
    else if( (p_oldvar = *pp_var) == p_var ) /* Variable create */
        p_var = NULL; /* Variable created */
    else /* Variable already exists */
    {
        assert (((i_type ^ p_oldvar->i_type) & VLC_VAR_CLASS) == 0);
        p_oldvar->i_usage++;
        p_oldvar->i_type |= i_type & VLC_VAR_ISCOMMAND;
    }
    vlc_mutex_unlock( &p_priv->var_lock );

    /* If we did not need to create a new variable, free everything... */
    if( p_var != NULL )
        Destroy( p_var );
    return ret;
}

void (var_Destroy)(vlc_object_t *p_this, const char *psz_name)
{
    variable_t *p_var;

    assert( p_this );

    vlc_object_internals_t *p_priv = vlc_internals( p_this );

    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
        msg_Dbg( p_this, "attempt to destroy nonexistent variable \"%s\"",
                 psz_name );
    else if( --p_var->i_usage == 0 )
    {
        assert(!p_var->b_incallback);
        tdelete( p_var, &p_priv->var_root, varcmp );
    }
    else
    {
        assert(p_var->i_usage != -1u);
        p_var = NULL;
    }
    vlc_mutex_unlock( &p_priv->var_lock );

    if( p_var != NULL )
        Destroy( p_var );
}

static void CleanupVar( void *var )
{
    Destroy( var );
}

void var_DestroyAll( vlc_object_t *obj )
{
    vlc_object_internals_t *priv = vlc_internals( obj );

    tdestroy( priv->var_root, CleanupVar );
    priv->var_root = NULL;
}

int (var_Change)(vlc_object_t *p_this, const char *psz_name, int i_action, ...)
{
    va_list ap;
    int ret = VLC_SUCCESS;
    variable_t *p_var;
    vlc_value_t oldval;
    vlc_value_t newval;

    assert( p_this );

    vlc_object_internals_t *p_priv = vlc_internals( p_this );

    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
    {
        vlc_mutex_unlock( &p_priv->var_lock );
        return VLC_ENOENT;
    }

    va_start(ap, i_action);
    switch( i_action )
    {
        case VLC_VAR_GETMIN:
            *va_arg(ap, vlc_value_t *) = p_var->min;
            break;
        case VLC_VAR_GETMAX:
            *va_arg(ap, vlc_value_t *) = p_var->max;
            break;
        case VLC_VAR_SETMINMAX:
            assert(p_var->ops->pf_free == FreeDummy);
            p_var->min = va_arg(ap, vlc_value_t);
            p_var->max = va_arg(ap, vlc_value_t);
            break;
        case VLC_VAR_SETSTEP:
            assert(p_var->ops->pf_free == FreeDummy);
            p_var->step = va_arg(ap, vlc_value_t);
            CheckValue( p_var, &p_var->val );
            break;
        case VLC_VAR_GETSTEP:
            switch (p_var->i_type & VLC_VAR_TYPE)
            {
                case VLC_VAR_INTEGER:
                    if (p_var->step.i_int == 0)
                        ret = VLC_EGENERIC;
                    break;
                case VLC_VAR_FLOAT:
                    if (p_var->step.f_float == 0.f)
                        ret = VLC_EGENERIC;
                    break;
                default:
                    ret = VLC_EGENERIC;
            }
            if (ret == VLC_SUCCESS)
                *va_arg(ap, vlc_value_t *) = p_var->step;
            break;
        case VLC_VAR_ADDCHOICE:
        {
            vlc_value_t val = va_arg(ap, vlc_value_t);
            const char *text = va_arg(ap, const char *);
            size_t count = p_var->choices_count;

            TAB_APPEND(p_var->choices_count, p_var->choices, val);
            p_var->ops->pf_dup(&p_var->choices[count]);
            TAB_APPEND(count, p_var->choices_text, NULL);
            assert(count == p_var->choices_count);
            if (text != NULL)
                p_var->choices_text[count - 1] = strdup(text);

            TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_ADDCHOICE,
                                &val);
            break;
        }
        case VLC_VAR_DELCHOICE:
        {
            vlc_value_t val = va_arg(ap, vlc_value_t);
            size_t count = p_var->choices_count, i;

            for (i = 0; i < count; i++)
                if (p_var->ops->pf_cmp(p_var->choices[i], val) == 0)
                    break;

            if (i == count)
            {   /* Not found */
                ret = VLC_EGENERIC;
                break;
            }

            p_var->ops->pf_free(&p_var->choices[i]);
            free(p_var->choices_text[i]);
            TAB_ERASE(p_var->choices_count, p_var->choices, i);
            TAB_ERASE(count, p_var->choices_text, i);
            assert(count == p_var->choices_count);

            TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_DELCHOICE,
                                &val);
            break;
        }
        case VLC_VAR_CHOICESCOUNT:
            *va_arg(ap, size_t *) = p_var->choices_count;
            break;
        case VLC_VAR_CLEARCHOICES:
            for (size_t i = 0; i < p_var->choices_count; i++)
                p_var->ops->pf_free(&p_var->choices[i]);
            for (size_t i = 0; i < p_var->choices_count; i++)
                free(p_var->choices_text[i]);
            TAB_CLEAN(p_var->choices_count, p_var->choices);
            free(p_var->choices_text);
            p_var->choices_text = NULL;

            TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_CLEARCHOICES, NULL);
            break;
        case VLC_VAR_SETVALUE:
            /* Duplicate data if needed */
            newval = va_arg(ap, vlc_value_t);
            p_var->ops->pf_dup( &newval );
            /* Backup needed stuff */
            oldval = p_var->val;
            /* Check boundaries and list */
            CheckValue( p_var, &newval );
            /* Set the variable */
            p_var->val = newval;
            /* Free data if needed */
            p_var->ops->pf_free( &oldval );
            break;
        case VLC_VAR_GETCHOICES:
        {
            size_t *count = va_arg(ap, size_t *);
            vlc_value_t **values = va_arg(ap, vlc_value_t **);
            char ***texts = va_arg(ap, char ***);

            *count = p_var->choices_count;
            *values = xmalloc(p_var->choices_count * sizeof (**values));

            for (size_t i = 0; i < p_var->choices_count; i++)
            {
                vlc_value_t *val = (*values) + i;
                *val = p_var->choices[i];
                p_var->ops->pf_dup(val);
            }

            if( texts != NULL )
            {
                char **tab = xmalloc(p_var->choices_count * sizeof (*tab));
                *texts = tab;

                for (size_t i = 0; i < p_var->choices_count; i++)
                    tab[i] = (p_var->choices_text[i] != NULL)
                        ? strdup(p_var->choices_text[i]) : NULL;
            }
            break;
        }
        case VLC_VAR_SETTEXT:
        {
            const char *text = va_arg(ap, const char *);

            free( p_var->psz_text );
            p_var->psz_text = (text != NULL) ? strdup(text) : NULL;
            break;
        }
        case VLC_VAR_GETTEXT:
            *va_arg(ap, char **) = (p_var->psz_text != NULL)
                ? strdup(p_var->psz_text) : NULL;
            break;
        default:
            break;
    }
    va_end(ap);
    vlc_mutex_unlock( &p_priv->var_lock );
    return ret;
}

int (var_GetAndSet)(vlc_object_t *p_this, const char *psz_name, int i_action,
                    vlc_value_t *p_val)
{
    variable_t *p_var;
    vlc_value_t oldval;

    assert( p_this );
    assert( p_val );

    vlc_object_internals_t *p_priv = vlc_internals( p_this );

    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
    {
        vlc_mutex_unlock( &p_priv->var_lock );
        return VLC_ENOENT;
    }

    WaitUnused( p_this, p_var );

    /* Duplicated data if needed */
    //p_var->ops->pf_dup( &val );

    /* Backup needed stuff */
    oldval = p_var->val;

    /* depending of the action required */
    switch( i_action )
    {
    case VLC_VAR_BOOL_TOGGLE:
        assert( ( p_var->i_type & VLC_VAR_BOOL ) == VLC_VAR_BOOL );
        p_var->val.b_bool = !p_var->val.b_bool;
        break;
    case VLC_VAR_INTEGER_ADD:
        assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
        p_var->val.i_int += p_val->i_int;
        break;
    case VLC_VAR_INTEGER_OR:
        assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
        p_var->val.i_int |= p_val->i_int;
        break;
    case VLC_VAR_INTEGER_NAND:
        assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
        p_var->val.i_int &= ~p_val->i_int;
        break;
    default:
        vlc_mutex_unlock( &p_priv->var_lock );
        return VLC_EGENERIC;
    }

    /*  Check boundaries */
    CheckValue( p_var, &p_var->val );
    *p_val = p_var->val;

    /* Deal with callbacks.*/
    TriggerCallback( p_this, p_var, psz_name, oldval );

    vlc_mutex_unlock( &p_priv->var_lock );
    return VLC_SUCCESS;
}

int (var_Type)(vlc_object_t *p_this, const char *psz_name)
{
    variable_t *p_var;
    int i_type = 0;

    assert( p_this );

    vlc_object_internals_t *p_priv = vlc_internals( p_this );

    p_var = Lookup( p_this, psz_name );
    if( p_var != NULL )
    {
        i_type = p_var->i_type;
        if (p_var->choices_count > 0)
            i_type |= VLC_VAR_HASCHOICE;
    }
    vlc_mutex_unlock( &p_priv->var_lock );

    return i_type;
}

int (var_SetChecked)(vlc_object_t *p_this, const char *psz_name,
                     int expected_type, vlc_value_t val)
{
    variable_t *p_var;
    vlc_value_t oldval;

    assert( p_this );

    vlc_object_internals_t *p_priv = vlc_internals( p_this );

    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
    {
        vlc_mutex_unlock( &p_priv->var_lock );
        return VLC_ENOENT;
    }

    assert( expected_type == 0 ||
            (p_var->i_type & VLC_VAR_CLASS) == expected_type );
    assert ((p_var->i_type & VLC_VAR_CLASS) != VLC_VAR_VOID);

    WaitUnused( p_this, p_var );

    /* Duplicate data if needed */
    p_var->ops->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 */
    TriggerCallback( p_this, p_var, psz_name, oldval );

    /* Free data if needed */
    p_var->ops->pf_free( &oldval );

    vlc_mutex_unlock( &p_priv->var_lock );
    return VLC_SUCCESS;
}

int (var_Set)(vlc_object_t *p_this, const char *psz_name, vlc_value_t val)
{
    return var_SetChecked( p_this, psz_name, 0, val );
}

int (var_GetChecked)(vlc_object_t *p_this, const char *psz_name,
                     int expected_type, vlc_value_t *p_val)
{
    assert( p_this );

    vlc_object_internals_t *p_priv = vlc_internals( p_this );
    variable_t *p_var;
    int err = VLC_SUCCESS;

    p_var = Lookup( p_this, psz_name );
    if( p_var != NULL )
    {
        assert( expected_type == 0 ||
                (p_var->i_type & VLC_VAR_CLASS) == expected_type );
        assert ((p_var->i_type & VLC_VAR_CLASS) != VLC_VAR_VOID);

        /* Really get the variable */
        *p_val = p_var->val;

        /* Duplicate value if needed */
        p_var->ops->pf_dup( p_val );
    }
    else
        err = VLC_ENOENT;

    vlc_mutex_unlock( &p_priv->var_lock );
    return err;
}

int (var_Get)(vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val)
{
    return var_GetChecked( p_this, psz_name, 0, p_val );
}

typedef enum
{
    vlc_value_callback,
    vlc_list_callback
} vlc_callback_type_t;

static void AddCallback( vlc_object_t *p_this, const char *psz_name,
                         callback_entry_t *restrict entry,
                         vlc_callback_type_t i_type )
{
    variable_t *p_var;

    assert( p_this );

    vlc_object_internals_t *p_priv = vlc_internals( p_this );

    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
    {
        vlc_mutex_unlock( &p_priv->var_lock );
        msg_Err( p_this, "cannot add callback %p to nonexistent variable '%s'",
                 entry->p_callback, psz_name );
        free( entry );
        return;
    }

    WaitUnused( p_this, p_var );

    callback_entry_t **pp;

    if (i_type == vlc_value_callback)
        pp = &p_var->value_callbacks;
    else
        pp = &p_var->list_callbacks;

    entry->next = *pp;
    *pp = entry;

    vlc_mutex_unlock( &p_priv->var_lock );
}

void (var_AddCallback)(vlc_object_t *p_this, const char *psz_name,
                       vlc_callback_t pf_callback, void *p_data)
{
    callback_entry_t *entry = xmalloc(sizeof (*entry));

    entry->pf_value_callback = pf_callback;
    entry->p_data = p_data;
    AddCallback(p_this, psz_name, entry, vlc_value_callback);
}

static void DelCallback( vlc_object_t *p_this, const char *psz_name,
                         const callback_entry_t *restrict match,
                         vlc_callback_type_t i_type )
{
    callback_entry_t **pp, *entry;
    variable_t *p_var;

    assert( p_this );

    vlc_object_internals_t *p_priv = vlc_internals( p_this );

    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
    {
        vlc_mutex_unlock( &p_priv->var_lock );
        msg_Err( p_this, "cannot delete callback %p from nonexistent "
                 "variable '%s'", match->p_callback, psz_name );
        return;
    }

    WaitUnused( p_this, p_var );

    if (i_type == vlc_value_callback)
        pp = &p_var->value_callbacks;
    else
        pp = &p_var->list_callbacks;

    entry = *pp;
    assert(entry != NULL);

    while (entry->p_callback != match->p_callback
        || entry->p_data != match->p_data)
    {
        pp = &entry->next;
        entry = *pp;
        assert(entry != NULL);
    }

    *pp = entry->next;
    vlc_mutex_unlock( &p_priv->var_lock );
    free(entry);
}

void (var_DelCallback)(vlc_object_t *p_this, const char *psz_name,
                       vlc_callback_t pf_callback, void *p_data)
{
    callback_entry_t entry;
    entry.pf_value_callback = pf_callback;
    entry.p_data = p_data;

    DelCallback(p_this, psz_name, &entry, vlc_value_callback);
}

void (var_TriggerCallback)(vlc_object_t *p_this, const char *psz_name)
{
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
    variable_t *p_var = Lookup( p_this, psz_name );
    if( p_var != NULL )
    {
        WaitUnused( p_this, p_var );

        /* Deal with callbacks. Tell we're in a callback, release the lock,
         * call stored functions, retake the lock. */

        TriggerCallback( p_this, p_var, psz_name, p_var->val );
    }
    vlc_mutex_unlock( &p_priv->var_lock );
}

void (var_AddListCallback)(vlc_object_t *p_this, const char *psz_name,
                           vlc_list_callback_t pf_callback, void *p_data)
{
    callback_entry_t *entry = xmalloc(sizeof (*entry));

    entry->pf_list_callback = pf_callback;
    entry->p_data = p_data;
    AddCallback(p_this, psz_name, entry, vlc_list_callback);
}

void (var_DelListCallback)(vlc_object_t *p_this, const char *psz_name,
                           vlc_list_callback_t pf_callback, void *p_data)
{
    callback_entry_t entry;

    entry.pf_list_callback = pf_callback;
    entry.p_data = p_data;
    DelCallback(p_this, psz_name, &entry, vlc_list_callback);
}

/** Parse a stringified option
 * This function parse a string option and create the associated object
 * variable
 * The option must be of the form "[no[-]]foo[=bar]" where foo is the
 * option name and bar is the value of the option.
 * \param p_obj the object in which the variable must be created
 * \param psz_option the option to parse
 * \param trusted whether the option is set by a trusted input or not
 */

void var_OptionParse( vlc_object_t *p_obj, const char *psz_option,
                      bool trusted )
{
    char *psz_name, *psz_value;
    int  i_type;
    bool b_isno = false;
    vlc_value_t val;

    val.psz_string = NULL;

    /* It's too much of a hassle to remove the ':' when we parse
     * the cmd line :) */

    if( psz_option[0] == ':' )
        psz_option++;

    if( !psz_option[0] )
        return;

    psz_name = strdup( psz_option );
    if( psz_name == NULL )
        return;

    psz_value = strchr( psz_name, '=' );
    if( psz_value != NULL )
        *psz_value++ = '\0';

    i_type = config_GetType( psz_name );
    if( !i_type && !psz_value )
    {
        /* check for "no-foo" or "nofoo" */
        if( !strncmp( psz_name, "no-", 3 ) )
        {
            memmove( psz_name, psz_name + 3, strlen(psz_name) + 1 - 3 );
        }
        else if( !strncmp( psz_name, "no", 2 ) )
        {
            memmove( psz_name, psz_name + 2, strlen(psz_name) + 1 - 2 );
        }
        else goto cleanup;           /* Option doesn't exist */

        b_isno = true;
        i_type = config_GetType( psz_name );
    }
    if( !i_type ) goto cleanup; /* Option doesn't exist */

    if( ( i_type != VLC_VAR_BOOL ) &&
        ( !psz_value || !*psz_value ) ) goto cleanup; /* Invalid value */

    /* check if option is unsafe */
    if( !trusted && !config_IsSafe( psz_name ) )
    {
        msg_Err( p_obj, "unsafe option \"%s\" has been ignored for "
                        "security reasons", psz_name );
        free( psz_name );
        return;
    }

    /* Create the variable in the input object.
     * Children of the input object will be able to retrieve this value
     * thanks to the inheritance property of the object variables. */

    var_Create( p_obj, psz_name, i_type );

    switch( i_type )
    {
    case VLC_VAR_BOOL:
        if( psz_value )
        {
            char *endptr;
            long long int value = strtoll( psz_value, &endptr, 0 );
            if( endptr == psz_value ) /* Not an integer */
                val.b_bool = strcasecmp( psz_value, "true" ) == 0
                          || strcasecmp( psz_value, "yes" ) == 0;
            else
                val.b_bool = value != 0;
        }
        else
            val.b_bool = !b_isno;
        break;

    case VLC_VAR_INTEGER:
        val.i_int = strtoll( psz_value, NULL, 0 );
        break;

    case VLC_VAR_FLOAT:
        val.f_float = vlc_atof_c( psz_value );
        break;

    case VLC_VAR_STRING:
        val.psz_string = psz_value;
        break;

    default:
        goto cleanup;
    }

    var_Set( p_obj, psz_name, val );

cleanup:
    free( psz_name );
}

int (var_LocationParse)(vlc_object_t *obj, const char *mrl, const char *pref)
{
    int ret = VLC_SUCCESS;
    size_t preflen = strlen (pref) + 1;

    assert(mrl != NULL);
    while (*mrl != '\0')
    {
        mrl += strspn (mrl, ":;"); /* skip leading colon(s) */

        size_t len = strcspn (mrl, ":;");
        char *buf = malloc (preflen + len);

        if (likely(buf != NULL))
        {
            /* NOTE: this does not support the "no-<varname>" bool syntax. */
            /* DO NOT use asprintf() here; it won't work! Think again. */
            snprintf (buf, preflen + len, "%s%s", pref, mrl);
            var_OptionParse (obj, buf, false);
            free (buf);
        }
        else
            ret = VLC_ENOMEM;
        mrl += len;
    }

    return ret;
}

int var_Inherit( vlc_object_t *p_this, const char *psz_name, int i_type,
                 vlc_value_t *p_val )
{
    i_type &= VLC_VAR_CLASS;
    for (vlc_object_t *obj = p_this; obj != NULL; obj = vlc_object_parent(obj))
    {
        if( var_GetChecked( obj, psz_name, i_type, p_val ) == VLC_SUCCESS )
            return VLC_SUCCESS;
    }

    /* else take value from config */
    switch( i_type & VLC_VAR_CLASS )
    {
        case VLC_VAR_STRING:
            p_val->psz_string = config_GetPsz( psz_name );
            break;
        case VLC_VAR_FLOAT:
            p_val->f_float = config_GetFloat( psz_name );
            break;
        case VLC_VAR_INTEGER:
            p_val->i_int = config_GetInt( psz_name );
            break;
        case VLC_VAR_BOOL:
            p_val->b_bool = config_GetInt( psz_name ) > 0;
            break;
        default:
            vlc_assert_unreachable();
        case VLC_VAR_ADDRESS:
            return VLC_ENOENT;
    }
    return VLC_SUCCESS;
}

int (var_InheritURational)(vlc_object_t *object,
                           unsigned *num, unsigned *den,
                           const char *var)
{
    char *str = var_InheritString(object, var);
    if (str == NULL)
        goto error;

    char *sep;
    unsigned n = strtoul(str, &sep, 10);
    unsigned d;

    switch (*sep) {
        case '\0':
            /* Decimal integer */
            d = 1;
            break;

        case ':':
        case '/':
            /* Decimal fraction */
            d = strtoul(sep + 1, &sep, 10);
            if (*sep != '\0')
                goto error;
            break;

        case '.': {
            /* Decimal number */
            unsigned char c;

            d = 1;
            while ((c = *(++sep)) != '\0') {
                c -= '0';

                if (c >= 10)
                    goto error;

                n = n * 10 + c;
                d *= 10;
            }
            break;
        }

        default:
            goto error;
    }

    free(str);

    if (n == 0) {
        *num = 0;
        *den = d ? 1 : 0;
    } else if (d == 0) {
        *num = 1;
        *den = 0;
    } else
        vlc_ureduce(num, den, n, d, 0);

    return VLC_SUCCESS;

error:
    free(str);
    *num = 0;
    *den = 0;
    return VLC_EGENERIC;
}

static thread_local void *twalk_ctx;

static void TwalkGetNames(const void *data, const VISIT which, const int depth)
{
    if (which != postorder && which != leaf)
        return;
    (void) depth;

    const variable_t *var = *(const variable_t **)data;
    DECL_ARRAY(char *) *names = twalk_ctx;
    char *dup = strdup(var->psz_name);
    if (dup != NULL)
        ARRAY_APPEND(*names, dup);
}

char **var_GetAllNames(vlc_object_t *obj)
{
    vlc_object_internals_t *priv = vlc_internals(obj);

    DECL_ARRAY(char *) names;
    ARRAY_INIT(names);

    twalk_ctx = &names;
    vlc_mutex_lock(&priv->var_lock);
    twalk(priv->var_root, TwalkGetNames);
    vlc_mutex_unlock(&priv->var_lock);

    if (names.i_size == 0)
        return NULL;
    ARRAY_APPEND(names, NULL);
    return names.p_elems;
}

Messung
CHZG
849610093

¤ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.