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