1 /*****************************************************************************
2 * chain.c : configuration module chain parsing stuff
3 *****************************************************************************
4 * Copyright (C) 2002-2007 the VideoLAN team
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * Laurent Aimar <fenrir@via.ecp.fr>
9 * Eric Petit <titer@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
37 #include "vlc_interface.h"
39 /*****************************************************************************
41 *****************************************************************************/
42 static bool IsEscapeNeeded( char c )
44 return c == '\'' || c == '"' || c == '\\';
46 static bool IsEscape( const char *psz )
50 return psz[0] == '\\' && IsEscapeNeeded( psz[1] );
52 static bool IsSpace( char c )
54 return c == ' ' || c == '\t';
57 #define SKIPSPACE( p ) do { while( *p && IsSpace( *p ) ) p++; } while(0)
58 #define SKIPTRAILINGSPACE( p, e ) \
59 do { while( e > p && IsSpace( *(e-1) ) ) e--; } while(0)
62 * This function will return a pointer after the end of a string element.
63 * It will search the closing element which is
64 * } for { (it will handle nested { ... })
68 static const char *ChainGetEnd( const char *psz_string )
70 const char *p = psz_string;
76 /* Look for a opening character */
81 if( *p == '\0' || *p == ',' || *p == '}' )
84 if( *p == '{' || *p == '"' || *p == '\'' )
88 /* Set c to the closing character */
95 /* Search the closing character, handle nested {..} */
105 else if( *p == '{' && c == '}' )
106 p = ChainGetEnd( p );
113 * It will extract an option value (=... or {...}).
114 * It will remove the initial = if present but keep the {}
116 static char *ChainGetValue( const char **ppsz_string )
118 const char *p = *ppsz_string;
120 char *psz_value = NULL;
122 bool b_keep_brackets = (*p == '{');
127 end = ChainGetEnd( p );
134 /* Skip heading and trailing spaces.
135 * This ain't necessary but will avoid simple
146 if( *p == '\'' || *p == '"' || ( !b_keep_brackets && *p == '{' ) )
150 if( *(end-1) != '\'' && *(end-1) == '"' )
151 SKIPTRAILINGSPACE( p, end );
156 psz_value = strndup( p, end -1 - p );
160 SKIPTRAILINGSPACE( p, end );
164 psz_value = strndup( p, end - p );
170 config_StringUnescape( psz_value );
177 char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg, const char *psz_chain )
179 config_chain_t *p_cfg = NULL;
180 const char *p = psz_chain;
188 /* Look for parameter(either a {...} or :...) or the end of name (space or nul) */
191 while( *p && *p != '{' && *p != ':' && !IsSpace( *p ) )
197 /* Extract the name */
198 *ppsz_name = strndup( psz_chain, p - psz_chain );
200 /* Parse the parameters */
205 const char *psz_name;
207 /* Skip the opening '{' */
210 /* parse all name=value[,] elements */
217 /* Look for the end of the name (,={}_space_) */
218 while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' && !IsSpace( *p ) )
221 // fprintf( stderr, "name=%s - rest=%s\n", psz_name, p );
224 fprintf( stderr, "config_ChainCreate: invalid options (empty) \n" );
230 cfg.psz_name = strndup( psz_name, p - psz_name );
231 cfg.psz_value = NULL;
234 /* Parse the option name parameter */
237 if( *p == '=' || *p == '{' )
239 cfg.psz_value = ChainGetValue( &p );
244 /* Append the new option */
245 config_chain_t *p_new = malloc( sizeof(*p_new) );
248 free( cfg.psz_name );
249 free( cfg.psz_value );
256 p_cfg->p_next = p_new;
257 p_cfg = p_cfg->p_next;
261 *pp_cfg = p_cfg = p_new;
277 return strdup( &p[1] );
282 void config_ChainDestroy( config_chain_t *p_cfg )
284 while( p_cfg != NULL )
286 config_chain_t *p_next;
288 p_next = p_cfg->p_next;
290 FREENULL( p_cfg->psz_name );
291 FREENULL( p_cfg->psz_value );
298 void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
299 const char *const *ppsz_options, config_chain_t *cfg )
301 if( psz_prefix == NULL ) psz_prefix = "";
302 size_t plen = 1 + strlen( psz_prefix );
304 /* First, var_Create all variables */
305 for( size_t i = 0; ppsz_options[i] != NULL; i++ )
307 const char *optname = ppsz_options[i];
308 if (optname[0] == '*')
311 char name[plen + strlen( optname )];
312 snprintf( name, sizeof (name), "%s%s", psz_prefix, optname );
313 if( var_Create( p_this, name,
314 config_GetType( p_this, name ) | VLC_VAR_DOINHERIT ) )
315 return /* VLC_xxx */;
318 /* Now parse options and set value */
319 for(; cfg; cfg = cfg->p_next )
324 module_config_t *p_conf;
328 if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
331 for( i = 0; ppsz_options[i] != NULL; i++ )
333 if( !strcmp( ppsz_options[i], cfg->psz_name ) )
337 if( ( !strncmp( cfg->psz_name, "no-", 3 ) &&
338 !strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) ||
339 ( !strncmp( cfg->psz_name, "no", 2 ) &&
340 !strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
346 if( *ppsz_options[i] == '*' &&
347 !strcmp( &ppsz_options[i][1], cfg->psz_name ) )
355 if( ppsz_options[i] == NULL )
357 msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
362 char name[plen + strlen( ppsz_options[i] )];
363 const char *psz_name = name;
364 snprintf( name, sizeof (name), "%s%s", psz_prefix,
365 b_once ? (ppsz_options[i] + 1) : ppsz_options[i] );
367 /* Check if the option is deprecated */
368 p_conf = config_FindConfig( p_this, name );
370 /* This is basically cut and paste from src/misc/configuration.c
371 * with slight changes */
374 if( p_conf->b_removed )
376 msg_Err( p_this, "Option %s is not supported anymore.",
378 /* TODO: this should return an error and end option parsing
379 * ... but doing this would change the VLC API and all the
380 * modules so i'll do it later */
383 if( p_conf->psz_oldname
384 && !strcmp( p_conf->psz_oldname, name ) )
386 psz_name = p_conf->psz_name;
387 msg_Warn( p_this, "Option %s is obsolete. Use %s instead.",
391 /* </Check if the option is deprecated> */
393 /* get the type of the variable */
394 i_type = config_GetType( p_this, psz_name );
397 msg_Warn( p_this, "unknown option %s (value=%s)",
398 cfg->psz_name, cfg->psz_value );
402 i_type &= CONFIG_ITEM;
404 if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
406 msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
409 if( i_type != VLC_VAR_STRING && b_once )
411 msg_Warn( p_this, "*option_name need to be a string option" );
420 case VLC_VAR_INTEGER:
421 val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
425 val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
429 val.psz_string = cfg->psz_value;
432 msg_Warn( p_this, "unhandled config var type (%d)", i_type );
433 memset( &val, 0, sizeof( vlc_value_t ) );
440 var_Get( p_this, psz_name, &val2 );
441 if( *val2.psz_string )
443 free( val2.psz_string );
444 msg_Dbg( p_this, "ignoring option %s (not first occurrence)", psz_name );
447 free( val2.psz_string );
449 var_Set( p_this, psz_name, val );
450 msg_Dbg( p_this, "set config option: %s to %s", psz_name,
451 cfg->psz_value ? cfg->psz_value : "(null)" );
455 char *config_StringUnescape( char *psz_string )
457 char *psz_src = psz_string;
458 char *psz_dst = psz_string;
464 if( IsEscape( psz_src ) )
466 *psz_dst++ = *psz_src++;
473 char *config_StringEscape( const char *psz_string )
483 for( const char *p = psz_string; *p; p++ )
485 if( IsEscapeNeeded( *p ) )
489 psz_return = psz_dst = malloc( strlen( psz_string ) + i_escape + 1 );
490 for( const char *p = psz_string; *p; p++ )
492 if( IsEscapeNeeded( *p ) )