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