/***************************************************************************** * chain.c : configuration module chain parsing stuff ***************************************************************************** * Copyright (C) 2002-2007 the VideoLAN team * $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 *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "vlc_common.h" #include "libvlc.h" #include "vlc_interface.h" /***************************************************************************** * 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 { } */ static const char *_get_chain_end( const char *str ) { char c; const char *p = str; 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++; } } } char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg, const char *psz_chain ) { config_chain_t *p_cfg = NULL; const char *p = psz_chain; *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 == '{' ) { const char *psz_name; 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 ) { fprintf( stderr, "config_ChainCreate: invalid options (empty) \n" ); break; } cfg.psz_name = strndup( psz_name, p - psz_name ); SKIPSPACE( p ); if( *p == '=' || *p == '{' ) { const char *end; bool b_keep_brackets = (*p == '{'); 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; } } void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix, const char *const *ppsz_options, config_chain_t *cfg ) { if( psz_prefix == NULL ) psz_prefix = ""; size_t plen = 1 + strlen( psz_prefix ); /* First, var_Create all variables */ for( size_t i = 0; ppsz_options[i] != NULL; i++ ) { 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 ) ) return /* VLC_xxx */; } /* Now parse options and set value */ for(; cfg; cfg = cfg->p_next ) { vlc_value_t val; bool b_yes = true; bool b_once = false; module_config_t *p_conf; int i_type; size_t i; if( cfg->psz_name == NULL || *cfg->psz_name == '\0' ) continue; 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 ) ) ) { b_yes = false; break; } if( *ppsz_options[i] == '*' && !strcmp( &ppsz_options[i][1], cfg->psz_name ) ) { b_once = true; break; } } if( ppsz_options[i] == NULL ) { msg_Warn( p_this, "option %s is unknown", cfg->psz_name ); continue; } /* create name */ 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] ); /* Check if the option is deprecated */ p_conf = config_FindConfig( p_this, name ); /* This is basically cut and paste from src/misc/configuration.c * with slight changes */ if( p_conf ) { if( p_conf->b_removed ) { msg_Err( p_this, "Option %s is not supported anymore.", name ); /* 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 */ continue; } 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 ); } } /* </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 ); continue; } i_type &= CONFIG_ITEM; if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL ) { msg_Warn( p_this, "missing value for option %s", cfg->psz_name ); continue; } if( i_type != VLC_VAR_STRING && b_once ) { msg_Warn( p_this, "*option_name need to be a string option" ); continue; } 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: msg_Warn( p_this, "unhandled config var type (%d)", i_type ); 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 ); continue; } free( val2.psz_string ); } var_Set( p_this, psz_name, val ); msg_Dbg( p_this, "set config option: %s to %s", psz_name, cfg->psz_value ? cfg->psz_value : "(null)" ); } }