Fix spurious "please select network protocol manually" error
[vlc.git] / src / config / chain.c
blob13661e5bdf852b2339857e23e445ea4968909fb6
1 /*****************************************************************************
2 * chain.c : configuration module chain parsing stuff
3 *****************************************************************************
4 * Copyright (C) 2002-2007 the VideoLAN team
5 * $Id$
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 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include <vlc_common.h>
35 #include "libvlc.h"
37 #include "vlc_interface.h"
39 /*****************************************************************************
40 * Local prototypes
41 *****************************************************************************/
42 static bool IsEscapeNeeded( char c )
44 return c == '\'' || c == '"' || c == '\\';
46 static bool IsEscape( const char *psz )
48 if( !psz )
49 return false;
50 return psz[0] == '\\' && IsEscapeNeeded( psz[1] );
52 static bool IsSpace( char c )
54 return c == ' ' || c == '\t';
57 #define SKIPSPACE( p ) p += strspn( p, " \t" )
58 #define SKIPTRAILINGSPACE( p, e ) \
59 do { while( e > p && IsSpace( *(e-1) ) ) e--; } while(0)
61 /**
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 { ... })
65 * " for "
66 * ' for '
68 static const char *ChainGetEnd( const char *psz_string )
70 const char *p = psz_string;
71 char c;
73 if( !psz_string )
74 return NULL;
76 /* Look for a opening character */
77 SKIPSPACE( p );
79 for( ;; p++)
81 if( *p == '\0' || *p == ',' || *p == '}' )
82 return p;
84 if( *p == '{' || *p == '"' || *p == '\'' )
85 break;
88 /* Set c to the closing character */
89 if( *p == '{' )
90 c = '}';
91 else
92 c = *p;
93 p++;
95 /* Search the closing character, handle nested {..} */
96 for( ;; )
98 if( *p == '\0')
99 return p;
101 if( IsEscape( p ) )
102 p += 2;
103 else if( *p == c )
104 return ++p;
105 else if( *p == '{' && c == '}' )
106 p = ChainGetEnd( p );
107 else
108 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;
121 const char *end;
122 bool b_keep_brackets = (*p == '{');
124 if( *p == '=' )
125 p++;
127 end = ChainGetEnd( p );
128 if( end <= p )
130 psz_value = NULL;
132 else
134 /* Skip heading and trailing spaces.
135 * This ain't necessary but will avoid simple
136 * user mistakes. */
137 SKIPSPACE( p );
140 if( end <= p )
142 psz_value = NULL;
144 else
146 if( *p == '\'' || *p == '"' || ( !b_keep_brackets && *p == '{' ) )
148 p++;
150 if( *(end-1) != '\'' && *(end-1) == '"' )
151 SKIPTRAILINGSPACE( p, end );
153 if( end - 1 <= p )
154 psz_value = NULL;
155 else
156 psz_value = strndup( p, end -1 - p );
158 else
160 SKIPTRAILINGSPACE( p, end );
161 if( end <= p )
162 psz_value = NULL;
163 else
164 psz_value = strndup( p, end - p );
168 /* */
169 if( psz_value )
170 config_StringUnescape( psz_value );
172 /* */
173 *ppsz_string = end;
174 return psz_value;
177 char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg,
178 const char *psz_chain )
180 config_chain_t **pp_next = pp_cfg;
181 size_t len;
183 *ppsz_name = NULL;
184 *pp_cfg = NULL;
186 if( !psz_chain )
187 return NULL;
188 psz_chain += strspn( psz_chain, " \t" );
190 /* Look for parameter (a {...} or :...) or end of name (space or nul) */
191 len = strcspn( psz_chain, "{: \t" );
192 *ppsz_name = strndup( psz_chain, len );
193 psz_chain += len;
195 /* Parse the parameters */
196 psz_chain += strspn( psz_chain, " \t" );
197 if( *psz_chain == '{' )
199 /* Parse all name=value[,] elements */
202 psz_chain++; /* skip previous delimiter */
203 psz_chain += strspn( psz_chain, " \t" );
205 /* Look for the end of the name (,={}_space_) */
206 len = strcspn( psz_chain, "=,{} \t" );
207 if( len == 0 )
208 continue; /* ignore empty parameter */
210 /* Append the new parameter */
211 config_chain_t *p_cfg = malloc( sizeof(*p_cfg) );
212 if( !p_cfg )
213 break;
214 p_cfg->psz_name = strndup( psz_chain, len );
215 psz_chain += len;
216 p_cfg->psz_value = NULL;
217 p_cfg->p_next = NULL;
219 *pp_next = p_cfg;
220 pp_next = &p_cfg->p_next;
222 /* Extract the option value */
223 psz_chain += strspn( psz_chain, " \t" );
224 if( strchr( "={", *psz_chain ) )
226 p_cfg->psz_value = ChainGetValue( &psz_chain );
227 psz_chain += strspn( psz_chain, " \t" );
230 while( !memchr( "}", *psz_chain, 2 ) );
232 if( *psz_chain ) psz_chain++; /* skip '}' */;
233 psz_chain += strspn( psz_chain, " \t" );
236 if( *psz_chain == ':' )
237 return strdup( psz_chain + 1 );
239 return NULL;
242 void config_ChainDestroy( config_chain_t *p_cfg )
244 while( p_cfg != NULL )
246 config_chain_t *p_next;
248 p_next = p_cfg->p_next;
250 FREENULL( p_cfg->psz_name );
251 FREENULL( p_cfg->psz_value );
252 free( p_cfg );
254 p_cfg = p_next;
258 void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
259 const char *const *ppsz_options, config_chain_t *cfg )
261 if( psz_prefix == NULL ) psz_prefix = "";
262 size_t plen = 1 + strlen( psz_prefix );
264 /* First, var_Create all variables */
265 for( size_t i = 0; ppsz_options[i] != NULL; i++ )
267 const char *optname = ppsz_options[i];
268 if (optname[0] == '*')
269 optname++;
271 char name[plen + strlen( optname )];
272 snprintf( name, sizeof (name), "%s%s", psz_prefix, optname );
273 if( var_Create( p_this, name,
274 config_GetType( p_this, name ) | VLC_VAR_DOINHERIT ) )
275 return /* VLC_xxx */;
278 /* Now parse options and set value */
279 for(; cfg; cfg = cfg->p_next )
281 vlc_value_t val;
282 bool b_yes = true;
283 bool b_once = false;
284 module_config_t *p_conf;
285 int i_type;
286 size_t i;
288 if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
289 continue;
291 for( i = 0; ppsz_options[i] != NULL; i++ )
293 if( !strcmp( ppsz_options[i], cfg->psz_name ) )
295 break;
297 if( ( !strncmp( cfg->psz_name, "no-", 3 ) &&
298 !strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) ||
299 ( !strncmp( cfg->psz_name, "no", 2 ) &&
300 !strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
302 b_yes = false;
303 break;
306 if( *ppsz_options[i] == '*' &&
307 !strcmp( &ppsz_options[i][1], cfg->psz_name ) )
309 b_once = true;
310 break;
315 if( ppsz_options[i] == NULL )
317 msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
318 continue;
321 /* create name */
322 char name[plen + strlen( ppsz_options[i] )];
323 const char *psz_name = name;
324 snprintf( name, sizeof (name), "%s%s", psz_prefix,
325 b_once ? (ppsz_options[i] + 1) : ppsz_options[i] );
327 /* Check if the option is deprecated */
328 p_conf = config_FindConfig( p_this, name );
330 /* This is basically cut and paste from src/misc/configuration.c
331 * with slight changes */
332 if( p_conf )
334 if( p_conf->b_removed )
336 msg_Err( p_this, "Option %s is not supported anymore.",
337 name );
338 /* TODO: this should return an error and end option parsing
339 * ... but doing this would change the VLC API and all the
340 * modules so i'll do it later */
341 continue;
343 if( p_conf->psz_oldname
344 && !strcmp( p_conf->psz_oldname, name ) )
346 psz_name = p_conf->psz_name;
347 msg_Warn( p_this, "Option %s is obsolete. Use %s instead.",
348 name, psz_name );
351 /* </Check if the option is deprecated> */
353 /* get the type of the variable */
354 i_type = config_GetType( p_this, psz_name );
355 if( !i_type )
357 msg_Warn( p_this, "unknown option %s (value=%s)",
358 cfg->psz_name, cfg->psz_value );
359 continue;
362 i_type &= CONFIG_ITEM;
364 if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
366 msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
367 continue;
369 if( i_type != VLC_VAR_STRING && b_once )
371 msg_Warn( p_this, "*option_name need to be a string option" );
372 continue;
375 switch( i_type )
377 case VLC_VAR_BOOL:
378 val.b_bool = b_yes;
379 break;
380 case VLC_VAR_INTEGER:
381 val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
382 NULL, 0 );
383 break;
384 case VLC_VAR_FLOAT:
385 val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
386 break;
387 case VLC_VAR_STRING:
388 case VLC_VAR_MODULE:
389 val.psz_string = cfg->psz_value;
390 break;
391 default:
392 msg_Warn( p_this, "unhandled config var type (%d)", i_type );
393 memset( &val, 0, sizeof( vlc_value_t ) );
394 break;
396 if( b_once )
398 vlc_value_t val2;
400 var_Get( p_this, psz_name, &val2 );
401 if( *val2.psz_string )
403 free( val2.psz_string );
404 msg_Dbg( p_this, "ignoring option %s (not first occurrence)", psz_name );
405 continue;
407 free( val2.psz_string );
409 var_Set( p_this, psz_name, val );
410 msg_Dbg( p_this, "set config option: %s to %s", psz_name,
411 cfg->psz_value ? cfg->psz_value : "(null)" );
415 config_chain_t *config_ChainDuplicate( const config_chain_t *p_src )
417 config_chain_t *p_dst = NULL;
418 config_chain_t **pp_last = &p_dst;
420 for( ; p_src != NULL; p_src = p_src->p_next )
422 config_chain_t *p = malloc( sizeof(*p) );
423 if( !p )
424 break;
425 p->p_next = NULL;
426 p->psz_name = p_src->psz_name ? strdup( p_src->psz_name ) : NULL;
427 p->psz_value = p_src->psz_value ? strdup( p_src->psz_value ) : NULL;
429 *pp_last = p;
430 pp_last = &p->p_next;
432 return p_dst;
435 char *config_StringUnescape( char *psz_string )
437 char *psz_src = psz_string;
438 char *psz_dst = psz_string;
439 if( !psz_src )
440 return NULL;
442 while( *psz_src )
444 if( IsEscape( psz_src ) )
445 psz_src++;
446 *psz_dst++ = *psz_src++;
448 *psz_dst = '\0';
450 return psz_string;
453 char *config_StringEscape( const char *psz_string )
455 char *psz_return;
456 char *psz_dst;
457 int i_escape;
459 if( !psz_string )
460 return NULL;
462 i_escape = 0;
463 for( const char *p = psz_string; *p; p++ )
465 if( IsEscapeNeeded( *p ) )
466 i_escape++;
469 psz_return = psz_dst = malloc( strlen( psz_string ) + i_escape + 1 );
470 for( const char *p = psz_string; *p; p++ )
472 if( IsEscapeNeeded( *p ) )
473 *psz_dst++ = '\\';
474 *psz_dst++ = *p;
476 *psz_dst = '\0';
478 return psz_return;