1 /*****************************************************************************
2 * chain.c : configuration module chain parsing stuff
3 *****************************************************************************
4 * Copyright (C) 2002-2007 VLC authors and VideoLAN
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 it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
36 #include <vlc_charset.h>
37 #include <vlc_plugin.h>
39 #include "vlc_interface.h"
40 #include "configuration.h"
42 /*****************************************************************************
44 *****************************************************************************/
45 static bool IsEscapeNeeded( char c
)
47 return c
== '\'' || c
== '"' || c
== '\\';
49 static bool IsEscape( const char *psz
)
53 return psz
[0] == '\\' && IsEscapeNeeded( psz
[1] );
55 static bool IsSpace( char c
)
57 return c
== ' ' || c
== '\t';
60 #define SKIPSPACE( p ) p += strspn( p, " \t" )
61 #define SKIPTRAILINGSPACE( p, e ) \
62 do { while( e > p && IsSpace( *(e-1) ) ) e--; } while(0)
65 * This function will return a pointer after the end of a string element.
66 * It will search the closing element which is
67 * } for { (it will handle nested { ... })
71 static const char *ChainGetEnd( const char *psz_string
)
73 const char *p
= psz_string
;
79 /* Look for a opening character */
84 if( *p
== '\0' || *p
== ',' || *p
== '}' )
87 if( *p
== '{' || *p
== '"' || *p
== '\'' )
91 /* Set c to the closing character */
98 /* Search the closing character, handle nested {..} */
108 else if( *p
== '{' && c
== '}' )
109 p
= ChainGetEnd( p
);
116 * It will extract an option value (=... or {...}).
117 * It will remove the initial = if present but keep the {}
119 static char *ChainGetValue( const char **ppsz_string
)
121 const char *p
= *ppsz_string
;
123 char *psz_value
= NULL
;
125 bool b_keep_brackets
= (*p
== '{');
130 end
= ChainGetEnd( p
);
137 /* Skip heading and trailing spaces.
138 * This ain't necessary but will avoid simple
149 if( *p
== '\'' || *p
== '"' || ( !b_keep_brackets
&& *p
== '{' ) )
153 if( *(end
-1) != '\'' && *(end
-1) == '"' )
154 SKIPTRAILINGSPACE( p
, end
);
159 psz_value
= strndup( p
, end
-1 - p
);
163 SKIPTRAILINGSPACE( p
, end
);
167 psz_value
= strndup( p
, end
- p
);
173 config_StringUnescape( psz_value
);
180 /* Parse all name=value[,] elements */
181 const char *config_ChainParseOptions( config_chain_t
**pp_cfg
, const char *psz_opts
)
183 config_chain_t
**pp_next
= pp_cfg
;
188 psz_opts
++; /* skip previous delimiter */
189 SKIPSPACE( psz_opts
);
193 /* Look for the end of the name (,={}_space_) */
194 size_t len
= strcspn( psz_opts
, "=,{} \t" );
196 continue; /* ignore empty parameter */
198 /* Append the new parameter */
199 config_chain_t
*p_cfg
= malloc( sizeof(*p_cfg
) );
202 p_cfg
->psz_name
= strndup( psz_opts
, len
);
204 p_cfg
->psz_value
= NULL
;
205 p_cfg
->p_next
= NULL
;
208 pp_next
= &p_cfg
->p_next
;
210 /* Extract the option value */
211 SKIPSPACE( psz_opts
);
212 if( strchr( "={", *psz_opts
) )
214 p_cfg
->psz_value
= ChainGetValue( &psz_opts
);
215 SKIPSPACE( psz_opts
);
218 while( !memchr( "}", *psz_opts
, 2 ) );
220 if( *psz_opts
) psz_opts
++; /* skip '}' */;
221 SKIPSPACE( psz_opts
);
226 char *config_ChainCreate( char **ppsz_name
, config_chain_t
**pp_cfg
,
227 const char *psz_chain
)
236 SKIPSPACE( psz_chain
);
238 /* Look for parameter (a {...} or :...) or end of name (space or nul) */
239 len
= strcspn( psz_chain
, "{: \t" );
240 *ppsz_name
= strndup( psz_chain
, len
);
243 /* Parse the parameters */
244 SKIPSPACE( psz_chain
);
245 if( *psz_chain
== '{' )
246 psz_chain
= config_ChainParseOptions( pp_cfg
, psz_chain
);
248 if( *psz_chain
== ':' )
249 return strdup( psz_chain
+ 1 );
254 void config_ChainDestroy( config_chain_t
*p_cfg
)
256 while( p_cfg
!= NULL
)
258 config_chain_t
*p_next
;
260 p_next
= p_cfg
->p_next
;
262 FREENULL( p_cfg
->psz_name
);
263 FREENULL( p_cfg
->psz_value
);
270 #undef config_ChainParse
271 void config_ChainParse( vlc_object_t
*p_this
, const char *psz_prefix
,
272 const char *const *ppsz_options
, config_chain_t
*cfg
)
274 if( psz_prefix
== NULL
) psz_prefix
= "";
275 size_t plen
= 1 + strlen( psz_prefix
);
277 /* First, var_Create all variables */
278 for( size_t i
= 0; ppsz_options
[i
] != NULL
; i
++ )
280 const char *optname
= ppsz_options
[i
];
281 if (optname
[0] == '*')
284 char name
[plen
+ strlen( optname
)];
285 snprintf( name
, sizeof (name
), "%s%s", psz_prefix
, optname
);
286 if( var_Create( p_this
, name
,
287 config_GetType( name
) | VLC_VAR_DOINHERIT
) )
288 return /* VLC_xxx */;
290 module_config_t
* p_conf
= config_FindConfig( name
);
293 switch( CONFIG_CLASS( p_conf
->i_type
) )
295 case CONFIG_ITEM_INTEGER
:
296 var_Change( p_this
, name
, VLC_VAR_SETMINMAX
,
297 &(vlc_value_t
){ .i_int
= p_conf
->min
.i
},
298 &(vlc_value_t
){ .i_int
= p_conf
->max
.i
} );
300 case CONFIG_ITEM_FLOAT
:
301 var_Change( p_this
, name
, VLC_VAR_SETMINMAX
,
302 &(vlc_value_t
){ .f_float
= p_conf
->min
.f
},
303 &(vlc_value_t
){ .f_float
= p_conf
->max
.f
} );
309 /* Now parse options and set value */
310 for(; cfg
; cfg
= cfg
->p_next
)
315 module_config_t
*p_conf
;
319 if( cfg
->psz_name
== NULL
|| *cfg
->psz_name
== '\0' )
322 for( i
= 0; ppsz_options
[i
] != NULL
; i
++ )
324 if( !strcmp( ppsz_options
[i
], cfg
->psz_name
) )
328 if( ( !strncmp( cfg
->psz_name
, "no-", 3 ) &&
329 !strcmp( ppsz_options
[i
], cfg
->psz_name
+ 3 ) ) ||
330 ( !strncmp( cfg
->psz_name
, "no", 2 ) &&
331 !strcmp( ppsz_options
[i
], cfg
->psz_name
+ 2 ) ) )
337 if( *ppsz_options
[i
] == '*' &&
338 !strcmp( &ppsz_options
[i
][1], cfg
->psz_name
) )
346 if( ppsz_options
[i
] == NULL
)
348 msg_Warn( p_this
, "option %s is unknown", cfg
->psz_name
);
353 char name
[plen
+ strlen( ppsz_options
[i
] )];
354 const char *psz_name
= name
;
355 snprintf( name
, sizeof (name
), "%s%s", psz_prefix
,
356 b_once
? (ppsz_options
[i
] + 1) : ppsz_options
[i
] );
358 /* Check if the option is deprecated */
359 p_conf
= config_FindConfig( name
);
361 /* This is basically cut and paste from src/misc/configuration.c
362 * with slight changes */
365 if( p_conf
->b_removed
)
367 msg_Err( p_this
, "Option %s is not supported anymore.",
369 /* TODO: this should return an error and end option parsing
370 * ... but doing this would change the VLC API and all the
371 * modules so i'll do it later */
375 /* </Check if the option is deprecated> */
377 /* get the type of the variable */
378 i_type
= config_GetType( psz_name
);
381 msg_Warn( p_this
, "unknown option %s (value=%s)",
382 cfg
->psz_name
, cfg
->psz_value
);
386 if( i_type
!= VLC_VAR_BOOL
&& cfg
->psz_value
== NULL
)
388 msg_Warn( p_this
, "missing value for option %s", cfg
->psz_name
);
391 if( i_type
!= VLC_VAR_STRING
&& b_once
)
393 msg_Warn( p_this
, "*option_name need to be a string option" );
402 case VLC_VAR_INTEGER
:
403 val
.i_int
= strtol( cfg
->psz_value
? cfg
->psz_value
: "0",
407 val
.f_float
= us_atof( cfg
->psz_value
? cfg
->psz_value
: "0" );
410 val
.psz_string
= cfg
->psz_value
;
413 msg_Warn( p_this
, "unhandled config var type (%d)", i_type
);
414 memset( &val
, 0, sizeof( vlc_value_t
) );
421 var_Get( p_this
, psz_name
, &val2
);
422 if( *val2
.psz_string
)
424 free( val2
.psz_string
);
425 msg_Dbg( p_this
, "ignoring option %s (not first occurrence)", psz_name
);
428 free( val2
.psz_string
);
430 var_Set( p_this
, psz_name
, val
);
431 msg_Dbg( p_this
, "set config option: %s to %s", psz_name
,
432 cfg
->psz_value
? cfg
->psz_value
: "(null)" );
436 config_chain_t
*config_ChainDuplicate( const config_chain_t
*p_src
)
438 config_chain_t
*p_dst
= NULL
;
439 config_chain_t
**pp_last
= &p_dst
;
441 for( ; p_src
!= NULL
; p_src
= p_src
->p_next
)
443 config_chain_t
*p
= malloc( sizeof(*p
) );
447 p
->psz_name
= p_src
->psz_name
? strdup( p_src
->psz_name
) : NULL
;
448 p
->psz_value
= p_src
->psz_value
? strdup( p_src
->psz_value
) : NULL
;
451 pp_last
= &p
->p_next
;
456 char *config_StringUnescape( char *psz_string
)
458 char *psz_src
= psz_string
;
459 char *psz_dst
= psz_string
;
465 if( IsEscape( psz_src
) )
467 *psz_dst
++ = *psz_src
++;
474 char *config_StringEscape( const char *str
)
481 for( const char *p
= str
; *p
; p
++ )
482 length
+= IsEscapeNeeded( *p
) ? 2 : 1;
484 char *ret
= malloc( length
+ 1 ), *dst
= ret
;
486 if( unlikely( !ret
) )
489 for( const char *p
= str
; *p
; p
++ )
491 if( IsEscapeNeeded( *p
) )