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