chain.c 11.1 KB
Newer Older
1
/*****************************************************************************
2
 * chain.c : configuration module chain parsing stuff
3
 *****************************************************************************
4
 * Copyright (C) 2002-2007 the VideoLAN team
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 * $Id$
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
 *          Laurent Aimar <fenrir@via.ecp.fr>
 *          Eric Petit <titer@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU 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
 *****************************************************************************/

30 31 32 33
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

34
#include <vlc_common.h>
35
#include "libvlc.h"
36

37 38
#include "vlc_interface.h"

39 40 41 42 43 44 45 46 47 48 49 50
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

/* chain format:
    module{option=*:option=*}[:module{option=*:...}]
 */
#define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
#define SKIPTRAILINGSPACE( p, e ) \
    { while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; }

/* go accross " " and { } */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
51
static const char *_get_chain_end( const char *str )
52
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
53 54
    char c;
    const char *p = str;
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

    SKIPSPACE( p );

    for( ;; )
    {
        if( !*p || *p == ',' || *p == '}' ) return p;

        if( *p != '{' && *p != '"' && *p != '\'' )
        {
            p++;
            continue;
        }

        if( *p == '{' ) c = '}';
        else c = *p;
        p++;

        for( ;; )
        {
            if( !*p ) return p;

            if( *p == c ) return ++p;
            else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
            else p++;
        }
    }
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
83
char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg, const char *psz_chain )
84 85
{
    config_chain_t *p_cfg = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
86
    const char *p = psz_chain;
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

    *ppsz_name = NULL;
    *pp_cfg    = NULL;

    if( !p ) return NULL;

    SKIPSPACE( p );

    while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;

    if( p == psz_chain ) return NULL;

    *ppsz_name = strndup( psz_chain, p - psz_chain );

    SKIPSPACE( p );

    if( *p == '{' )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
105
        const char *psz_name;
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

        p++;

        for( ;; )
        {
            config_chain_t cfg;

            SKIPSPACE( p );

            psz_name = p;

            while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
                   *p != ' ' && *p != '\t' ) p++;

            /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
            if( p == psz_name )
            {
123
                fprintf( stderr, "config_ChainCreate: invalid options (empty) \n" );
124 125 126 127 128 129 130 131 132
                break;
            }

            cfg.psz_name = strndup( psz_name, p - psz_name );

            SKIPSPACE( p );

            if( *p == '=' || *p == '{' )
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
133
                const char *end;
134
                bool b_keep_brackets = (*p == '{');
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

                if( *p == '=' ) p++;

                end = _get_chain_end( p );
                if( end <= p )
                {
                    cfg.psz_value = NULL;
                }
                else
                {
                    /* Skip heading and trailing spaces.
                     * This ain't necessary but will avoid simple
                     * user mistakes. */
                    SKIPSPACE( p );
                }

                if( end <= p )
                {
                    cfg.psz_value = NULL;
                }
                else
                {
                    if( *p == '\'' || *p == '"' ||
                        ( !b_keep_brackets && *p == '{' ) )
                    {
                        p++;

                        if( *(end-1) != '\'' && *(end-1) == '"' )
                            SKIPTRAILINGSPACE( p, end );

                        if( end - 1 <= p ) cfg.psz_value = NULL;
                        else cfg.psz_value = strndup( p, end -1 - p );
                    }
                    else
                    {
                        SKIPTRAILINGSPACE( p, end );
                        if( end <= p ) cfg.psz_value = NULL;
                        else cfg.psz_value = strndup( p, end - p );
                    }
                }

                p = end;
                SKIPSPACE( p );
            }
            else
            {
                cfg.psz_value = NULL;
            }

            cfg.p_next = NULL;
            if( p_cfg )
            {
                p_cfg->p_next = malloc( sizeof( config_chain_t ) );
                memcpy( p_cfg->p_next, &cfg, sizeof( config_chain_t ) );

                p_cfg = p_cfg->p_next;
            }
            else
            {
                p_cfg = malloc( sizeof( config_chain_t ) );
                memcpy( p_cfg, &cfg, sizeof( config_chain_t ) );

                *pp_cfg = p_cfg;
            }

            if( *p == ',' ) p++;

            if( *p == '}' )
            {
                p++;
                break;
            }
        }
    }

    if( *p == ':' ) return( strdup( p + 1 ) );

    return NULL;
}

void config_ChainDestroy( config_chain_t *p_cfg )
{
    while( p_cfg != NULL )
    {
        config_chain_t *p_next;

        p_next = p_cfg->p_next;

        FREENULL( p_cfg->psz_name );
        FREENULL( p_cfg->psz_value );
        free( p_cfg );

        p_cfg = p_next;
    }
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
231
void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
232
                          const char *const *ppsz_options, config_chain_t *cfg )
233
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
234 235
    if( psz_prefix == NULL ) psz_prefix = "";
    size_t plen = 1 + strlen( psz_prefix );
236 237

    /* First, var_Create all variables */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
238
    for( size_t i = 0; ppsz_options[i] != NULL; i++ )
239
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
240 241 242 243 244 245 246 247
        const char *optname = ppsz_options[i];
        if (optname[0] == '*')
            optname++;

        char name[plen + strlen( optname )];
        snprintf( name, sizeof (name), "%s%s", psz_prefix, optname );
        if( var_Create( p_this, name,
                        config_GetType( p_this, name ) | VLC_VAR_DOINHERIT ) )
248
            return /* VLC_xxx */;
249 250 251
    }

    /* Now parse options and set value */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
252
    for(; cfg; cfg = cfg->p_next )
253 254
    {
        vlc_value_t val;
255 256
        bool b_yes = true;
        bool b_once = false;
257
        module_config_t *p_conf;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
258 259
        int i_type;
        size_t i;
260 261 262

        if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
            continue;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
263

264 265 266 267 268 269 270 271 272 273 274
        for( i = 0; ppsz_options[i] != NULL; i++ )
        {
            if( !strcmp( ppsz_options[i], cfg->psz_name ) )
            {
                break;
            }
            if( ( !strncmp( cfg->psz_name, "no-", 3 ) &&
                  !strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) ||
                ( !strncmp( cfg->psz_name, "no", 2 ) &&
                  !strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
            {
275
                b_yes = false;
276 277 278 279 280 281
                break;
            }

            if( *ppsz_options[i] == '*' &&
                !strcmp( &ppsz_options[i][1], cfg->psz_name ) )
            {
282
                b_once = true;
283 284 285 286
                break;
            }

        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
287

288 289 290 291 292 293 294
        if( ppsz_options[i] == NULL )
        {
            msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
            continue;
        }

        /* create name */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
295 296 297 298
        char name[plen + strlen( ppsz_options[i] )];
        const char *psz_name = name;
        snprintf( name, sizeof (name), "%s%s", psz_prefix,
                  b_once ? (ppsz_options[i] + 1) : ppsz_options[i] );
299 300

        /* Check if the option is deprecated */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
301
        p_conf = config_FindConfig( p_this, name );
302 303 304

        /* This is basically cut and paste from src/misc/configuration.c
         * with slight changes */
305
        if( p_conf )
306
        {
307
            if( p_conf->b_removed )
308
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
309
                msg_Err( p_this, "Option %s is not supported anymore.",
310
                         name );
311 312 313
                /* TODO: this should return an error and end option parsing
                 * ... but doing this would change the VLC API and all the
                 * modules so i'll do it later */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
314
                continue;
315
            }
316 317 318 319 320 321 322
            if( p_conf->psz_oldname
             && !strcmp( p_conf->psz_oldname, name ) )
            {
                 psz_name = p_conf->psz_name;
                 msg_Warn( p_this, "Option %s is obsolete. Use %s instead.",
                           name, psz_name );
            }
323 324 325 326 327 328 329 330 331
        }
        /* </Check if the option is deprecated> */

        /* get the type of the variable */
        i_type = config_GetType( p_this, psz_name );
        if( !i_type )
        {
            msg_Warn( p_this, "unknown option %s (value=%s)",
                      cfg->psz_name, cfg->psz_value );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
332
            continue;
333
        }
334

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
335
        i_type &= CONFIG_ITEM;
336

337 338 339
        if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
        {
            msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
340
            continue;
341 342 343 344
        }
        if( i_type != VLC_VAR_STRING && b_once )
        {
            msg_Warn( p_this, "*option_name need to be a string option" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
345
            continue;
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
        }

        switch( i_type )
        {
            case VLC_VAR_BOOL:
                val.b_bool = b_yes;
                break;
            case VLC_VAR_INTEGER:
                val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
                                    NULL, 0 );
                break;
            case VLC_VAR_FLOAT:
                val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
                break;
            case VLC_VAR_STRING:
            case VLC_VAR_MODULE:
                val.psz_string = cfg->psz_value;
                break;
            default:
365
                msg_Warn( p_this, "unhandled config var type (%d)", i_type );
366 367 368 369 370 371 372 373 374 375 376 377
                memset( &val, 0, sizeof( vlc_value_t ) );
                break;
        }
        if( b_once )
        {
            vlc_value_t val2;

            var_Get( p_this, psz_name, &val2 );
            if( *val2.psz_string )
            {
                free( val2.psz_string );
                msg_Dbg( p_this, "ignoring option %s (not first occurrence)", psz_name );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
378
                continue;
379 380 381 382
            }
            free( val2.psz_string );
        }
        var_Set( p_this, psz_name, val );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
383 384
        msg_Dbg( p_this, "set config option: %s to %s", psz_name,
                 cfg->psz_value ? cfg->psz_value : "(null)" );
385 386
    }
}