]> git.sesse.net Git - vlc/blob - src/config/chain.c
update module LIST file.
[vlc] / src / config / chain.c
1 /*****************************************************************************
2  * chain.c : configuration module chain parsing stuff
3  *****************************************************************************
4  * Copyright (C) 2002-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Eric Petit <titer@videolan.org>
10  *
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.
15  *
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.
20  *
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  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc/vlc.h>
35 #include "libvlc.h"
36
37 #include "vlc_interface.h"
38
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42
43 /* chain format:
44     module{option=*:option=*}[:module{option=*:...}]
45  */
46 #define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
47 #define SKIPTRAILINGSPACE( p, e ) \
48     { while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; }
49
50 /* go accross " " and { } */
51 static const char *_get_chain_end( const char *str )
52 {
53     char c;
54     const char *p = str;
55
56     SKIPSPACE( p );
57
58     for( ;; )
59     {
60         if( !*p || *p == ',' || *p == '}' ) return p;
61
62         if( *p != '{' && *p != '"' && *p != '\'' )
63         {
64             p++;
65             continue;
66         }
67
68         if( *p == '{' ) c = '}';
69         else c = *p;
70         p++;
71
72         for( ;; )
73         {
74             if( !*p ) return p;
75
76             if( *p == c ) return ++p;
77             else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
78             else p++;
79         }
80     }
81 }
82
83 char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg, const char *psz_chain )
84 {
85     config_chain_t *p_cfg = NULL;
86     const char *p = psz_chain;
87
88     *ppsz_name = NULL;
89     *pp_cfg    = NULL;
90
91     if( !p ) return NULL;
92
93     SKIPSPACE( p );
94
95     while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;
96
97     if( p == psz_chain ) return NULL;
98
99     *ppsz_name = strndup( psz_chain, p - psz_chain );
100
101     SKIPSPACE( p );
102
103     if( *p == '{' )
104     {
105         const char *psz_name;
106
107         p++;
108
109         for( ;; )
110         {
111             config_chain_t cfg;
112
113             SKIPSPACE( p );
114
115             psz_name = p;
116
117             while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
118                    *p != ' ' && *p != '\t' ) p++;
119
120             /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
121             if( p == psz_name )
122             {
123                 fprintf( stderr, "invalid options (empty)" );
124                 break;
125             }
126
127             cfg.psz_name = strndup( psz_name, p - psz_name );
128
129             SKIPSPACE( p );
130
131             if( *p == '=' || *p == '{' )
132             {
133                 const char *end;
134                 vlc_bool_t b_keep_brackets = (*p == '{');
135
136                 if( *p == '=' ) p++;
137
138                 end = _get_chain_end( p );
139                 if( end <= p )
140                 {
141                     cfg.psz_value = NULL;
142                 }
143                 else
144                 {
145                     /* Skip heading and trailing spaces.
146                      * This ain't necessary but will avoid simple
147                      * user mistakes. */
148                     SKIPSPACE( p );
149                 }
150
151                 if( end <= p )
152                 {
153                     cfg.psz_value = NULL;
154                 }
155                 else
156                 {
157                     if( *p == '\'' || *p == '"' ||
158                         ( !b_keep_brackets && *p == '{' ) )
159                     {
160                         p++;
161
162                         if( *(end-1) != '\'' && *(end-1) == '"' )
163                             SKIPTRAILINGSPACE( p, end );
164
165                         if( end - 1 <= p ) cfg.psz_value = NULL;
166                         else cfg.psz_value = strndup( p, end -1 - p );
167                     }
168                     else
169                     {
170                         SKIPTRAILINGSPACE( p, end );
171                         if( end <= p ) cfg.psz_value = NULL;
172                         else cfg.psz_value = strndup( p, end - p );
173                     }
174                 }
175
176                 p = end;
177                 SKIPSPACE( p );
178             }
179             else
180             {
181                 cfg.psz_value = NULL;
182             }
183
184             cfg.p_next = NULL;
185             if( p_cfg )
186             {
187                 p_cfg->p_next = malloc( sizeof( config_chain_t ) );
188                 memcpy( p_cfg->p_next, &cfg, sizeof( config_chain_t ) );
189
190                 p_cfg = p_cfg->p_next;
191             }
192             else
193             {
194                 p_cfg = malloc( sizeof( config_chain_t ) );
195                 memcpy( p_cfg, &cfg, sizeof( config_chain_t ) );
196
197                 *pp_cfg = p_cfg;
198             }
199
200             if( *p == ',' ) p++;
201
202             if( *p == '}' )
203             {
204                 p++;
205                 break;
206             }
207         }
208     }
209
210     if( *p == ':' ) return( strdup( p + 1 ) );
211
212     return NULL;
213 }
214
215 void config_ChainDestroy( config_chain_t *p_cfg )
216 {
217     while( p_cfg != NULL )
218     {
219         config_chain_t *p_next;
220
221         p_next = p_cfg->p_next;
222
223         FREENULL( p_cfg->psz_name );
224         FREENULL( p_cfg->psz_value );
225         free( p_cfg );
226
227         p_cfg = p_next;
228     }
229 }
230
231 void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
232                           const char *const *ppsz_options, config_chain_t *cfg )
233 {
234     if( psz_prefix == NULL ) psz_prefix = "";
235     size_t plen = 1 + strlen( psz_prefix );
236
237     /* First, var_Create all variables */
238     for( size_t i = 0; ppsz_options[i] != NULL; i++ )
239     {
240         const char *optname = ppsz_options[i];
241         if (optname[0] == '*')
242             optname++;
243
244         char name[plen + strlen( optname )];
245         snprintf( name, sizeof (name), "%s%s", psz_prefix, optname );
246         if( var_Create( p_this, name,
247                         config_GetType( p_this, name ) | VLC_VAR_DOINHERIT ) )
248             return /* VLC_xxx */;
249     }
250
251     /* Now parse options and set value */
252     for(; cfg; cfg = cfg->p_next )
253     {
254         vlc_value_t val;
255         vlc_bool_t b_yes = VLC_TRUE;
256         vlc_bool_t b_once = VLC_FALSE;
257         module_config_t *p_conf;
258         int i_type;
259         size_t i;
260
261         if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
262             continue;
263
264         for( i = 0; ppsz_options[i] != NULL; i++ )
265         {
266             if( !strcmp( ppsz_options[i], cfg->psz_name ) )
267             {
268                 break;
269             }
270             if( ( !strncmp( cfg->psz_name, "no-", 3 ) &&
271                   !strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) ||
272                 ( !strncmp( cfg->psz_name, "no", 2 ) &&
273                   !strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
274             {
275                 b_yes = VLC_FALSE;
276                 break;
277             }
278
279             if( *ppsz_options[i] == '*' &&
280                 !strcmp( &ppsz_options[i][1], cfg->psz_name ) )
281             {
282                 b_once = VLC_TRUE;
283                 break;
284             }
285
286         }
287
288         if( ppsz_options[i] == NULL )
289         {
290             msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
291             continue;
292         }
293
294         /* create name */
295         char name[plen + strlen( ppsz_options[i] )];
296         const char *psz_name = name;
297         snprintf( name, sizeof (name), "%s%s", psz_prefix,
298                   b_once ? (ppsz_options[i] + 1) : ppsz_options[i] );
299
300         /* Check if the option is deprecated */
301         p_conf = config_FindConfig( p_this, name );
302
303         /* This is basically cut and paste from src/misc/configuration.c
304          * with slight changes */
305         if( p_conf )
306         {
307             if( p_conf->b_removed )
308             {
309                 msg_Err( p_this, "Option %s is not supported anymore.",
310                          name );
311                 /* TODO: this should return an error and end option parsing
312                  * ... but doing this would change the VLC API and all the
313                  * modules so i'll do it later */
314                 continue;
315             }
316             if( p_conf->psz_oldname
317              && !strcmp( p_conf->psz_oldname, name ) )
318             {
319                  psz_name = p_conf->psz_name;
320                  msg_Warn( p_this, "Option %s is obsolete. Use %s instead.",
321                            name, psz_name );
322             }
323         }
324         /* </Check if the option is deprecated> */
325
326         /* get the type of the variable */
327         i_type = config_GetType( p_this, psz_name );
328         if( !i_type )
329         {
330             msg_Warn( p_this, "unknown option %s (value=%s)",
331                       cfg->psz_name, cfg->psz_value );
332             continue;
333         }
334
335         i_type &= CONFIG_ITEM;
336
337         if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
338         {
339             msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
340             continue;
341         }
342         if( i_type != VLC_VAR_STRING && b_once )
343         {
344             msg_Warn( p_this, "*option_name need to be a string option" );
345             continue;
346         }
347
348         switch( i_type )
349         {
350             case VLC_VAR_BOOL:
351                 val.b_bool = b_yes;
352                 break;
353             case VLC_VAR_INTEGER:
354                 val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
355                                     NULL, 0 );
356                 break;
357             case VLC_VAR_FLOAT:
358                 val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
359                 break;
360             case VLC_VAR_STRING:
361             case VLC_VAR_MODULE:
362                 val.psz_string = cfg->psz_value;
363                 break;
364             default:
365                 msg_Warn( p_this, "unhandled config var type (%d)", i_type );
366                 memset( &val, 0, sizeof( vlc_value_t ) );
367                 break;
368         }
369         if( b_once )
370         {
371             vlc_value_t val2;
372
373             var_Get( p_this, psz_name, &val2 );
374             if( *val2.psz_string )
375             {
376                 free( val2.psz_string );
377                 msg_Dbg( p_this, "ignoring option %s (not first occurrence)", psz_name );
378                 continue;
379             }
380             free( val2.psz_string );
381         }
382         var_Set( p_this, psz_name, val );
383         msg_Dbg( p_this, "set config option: %s to %s", psz_name,
384                  cfg->psz_value ? cfg->psz_value : "(null)" );
385     }
386 }