]> git.sesse.net Git - vlc/blob - src/config/chain.c
Move core Win32 files to their own directory
[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_common.h>
35 #include "libvlc.h"
36 #include <vlc_charset.h>
37
38 #include "vlc_interface.h"
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static bool IsEscapeNeeded( char c )
44 {
45     return c == '\'' || c == '"' || c == '\\';
46 }
47 static bool IsEscape( const char *psz )
48 {
49     if( !psz )
50         return false;
51     return psz[0] == '\\' && IsEscapeNeeded( psz[1] );
52 }
53 static bool IsSpace( char c  )
54 {
55     return c == ' ' || c == '\t';
56 }
57
58 #define SKIPSPACE( p ) p += strspn( p, " \t" )
59 #define SKIPTRAILINGSPACE( p, e ) \
60     do { while( e > p && IsSpace( *(e-1) ) ) e--; } while(0)
61
62 /**
63  * This function will return a pointer after the end of a string element.
64  * It will search the closing element which is
65  * } for { (it will handle nested { ... })
66  * " for "
67  * ' for '
68  */
69 static const char *ChainGetEnd( const char *psz_string )
70 {
71     const char *p = psz_string;
72     char c;
73
74     if( !psz_string )
75         return NULL;
76
77     /* Look for a opening character */
78     SKIPSPACE( p );
79
80     for( ;; p++)
81     {
82         if( *p == '\0' || *p == ',' || *p == '}' )
83             return p;
84
85         if( *p == '{' || *p == '"' || *p == '\'' )
86             break;
87     }
88
89     /* Set c to the closing character */
90     if( *p == '{' )
91         c = '}';
92     else
93         c = *p;
94     p++;
95
96     /* Search the closing character, handle nested {..} */
97     for( ;; )
98     {
99         if( *p == '\0')
100             return p;
101
102         if( IsEscape( p ) )
103             p += 2;
104         else if( *p == c )
105             return ++p;
106         else if( *p == '{' && c == '}' )
107             p = ChainGetEnd( p );
108         else
109             p++;
110     }
111 }
112
113 /**
114  * It will extract an option value (=... or {...}).
115  * It will remove the initial = if present but keep the {}
116  */
117 static char *ChainGetValue( const char **ppsz_string )
118 {
119     const char *p = *ppsz_string;
120
121     char *psz_value = NULL;
122     const char *end;
123     bool b_keep_brackets = (*p == '{');
124
125     if( *p == '=' )
126         p++;
127
128     end = ChainGetEnd( p );
129     if( end <= p )
130     {
131         psz_value = NULL;
132     }
133     else
134     {
135         /* Skip heading and trailing spaces.
136          * This ain't necessary but will avoid simple
137          * user mistakes. */
138         SKIPSPACE( p );
139     }
140
141     if( end <= p )
142     {
143         psz_value = NULL;
144     }
145     else
146     {
147         if( *p == '\'' || *p == '"' || ( !b_keep_brackets && *p == '{' ) )
148         {
149             p++;
150
151             if( *(end-1) != '\'' && *(end-1) == '"' )
152                 SKIPTRAILINGSPACE( p, end );
153
154             if( end - 1 <= p )
155                 psz_value = NULL;
156             else
157                 psz_value = strndup( p, end -1 - p );
158         }
159         else
160         {
161             SKIPTRAILINGSPACE( p, end );
162             if( end <= p )
163                 psz_value = NULL;
164             else
165                 psz_value = strndup( p, end - p );
166         }
167     }
168
169     /* */
170     if( psz_value )
171         config_StringUnescape( psz_value );
172
173     /* */
174     *ppsz_string = end;
175     return psz_value;
176 }
177
178 char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg,
179                           const char *psz_chain )
180 {
181     config_chain_t **pp_next = pp_cfg;
182     size_t len;
183
184     *ppsz_name = NULL;
185     *pp_cfg    = NULL;
186
187     if( !psz_chain )
188         return NULL;
189     SKIPSPACE( psz_chain );
190
191     /* Look for parameter (a {...} or :...) or end of name (space or nul) */
192     len = strcspn( psz_chain, "{: \t" );
193     *ppsz_name = strndup( psz_chain, len );
194     psz_chain += len;
195
196     /* Parse the parameters */
197     SKIPSPACE( psz_chain );
198     if( *psz_chain == '{' )
199     {
200         /* Parse all name=value[,] elements */
201         do
202         {
203             psz_chain++; /* skip previous delimiter */
204             SKIPSPACE( psz_chain );
205
206             /* Look for the end of the name (,={}_space_) */
207             len = strcspn( psz_chain, "=,{} \t" );
208             if( len == 0 )
209                 continue; /* ignore empty parameter */
210
211             /* Append the new parameter */
212             config_chain_t *p_cfg = malloc( sizeof(*p_cfg) );
213             if( !p_cfg )
214                 break;
215             p_cfg->psz_name = strndup( psz_chain, len );
216             psz_chain += len;
217             p_cfg->psz_value = NULL;
218             p_cfg->p_next = NULL;
219
220             *pp_next = p_cfg;
221             pp_next = &p_cfg->p_next;
222
223             /* Extract the option value */
224             SKIPSPACE( psz_chain );
225             if( strchr( "={", *psz_chain ) )
226             {
227                 p_cfg->psz_value = ChainGetValue( &psz_chain );
228                 SKIPSPACE( psz_chain );
229             }
230         }
231         while( !memchr( "}", *psz_chain, 2 ) );
232
233         if( *psz_chain ) psz_chain++; /* skip '}' */;
234         SKIPSPACE( psz_chain );
235     }
236
237     if( *psz_chain == ':' )
238         return strdup( psz_chain + 1 );
239
240     return NULL;
241 }
242
243 void config_ChainDestroy( config_chain_t *p_cfg )
244 {
245     while( p_cfg != NULL )
246     {
247         config_chain_t *p_next;
248
249         p_next = p_cfg->p_next;
250
251         FREENULL( p_cfg->psz_name );
252         FREENULL( p_cfg->psz_value );
253         free( p_cfg );
254
255         p_cfg = p_next;
256     }
257 }
258
259 void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
260                           const char *const *ppsz_options, config_chain_t *cfg )
261 {
262     if( psz_prefix == NULL ) psz_prefix = "";
263     size_t plen = 1 + strlen( psz_prefix );
264
265     /* First, var_Create all variables */
266     for( size_t i = 0; ppsz_options[i] != NULL; i++ )
267     {
268         const char *optname = ppsz_options[i];
269         if (optname[0] == '*')
270             optname++;
271
272         char name[plen + strlen( optname )];
273         snprintf( name, sizeof (name), "%s%s", psz_prefix, optname );
274         if( var_Create( p_this, name,
275                         config_GetType( p_this, name ) | VLC_VAR_DOINHERIT ) )
276             return /* VLC_xxx */;
277     }
278
279     /* Now parse options and set value */
280     for(; cfg; cfg = cfg->p_next )
281     {
282         vlc_value_t val;
283         bool b_yes = true;
284         bool b_once = false;
285         module_config_t *p_conf;
286         int i_type;
287         size_t i;
288
289         if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
290             continue;
291
292         for( i = 0; ppsz_options[i] != NULL; i++ )
293         {
294             if( !strcmp( ppsz_options[i], cfg->psz_name ) )
295             {
296                 break;
297             }
298             if( ( !strncmp( cfg->psz_name, "no-", 3 ) &&
299                   !strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) ||
300                 ( !strncmp( cfg->psz_name, "no", 2 ) &&
301                   !strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
302             {
303                 b_yes = false;
304                 break;
305             }
306
307             if( *ppsz_options[i] == '*' &&
308                 !strcmp( &ppsz_options[i][1], cfg->psz_name ) )
309             {
310                 b_once = true;
311                 break;
312             }
313
314         }
315
316         if( ppsz_options[i] == NULL )
317         {
318             msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
319             continue;
320         }
321
322         /* create name */
323         char name[plen + strlen( ppsz_options[i] )];
324         const char *psz_name = name;
325         snprintf( name, sizeof (name), "%s%s", psz_prefix,
326                   b_once ? (ppsz_options[i] + 1) : ppsz_options[i] );
327
328         /* Check if the option is deprecated */
329         p_conf = config_FindConfig( p_this, name );
330
331         /* This is basically cut and paste from src/misc/configuration.c
332          * with slight changes */
333         if( p_conf )
334         {
335             if( p_conf->b_removed )
336             {
337                 msg_Err( p_this, "Option %s is not supported anymore.",
338                          name );
339                 /* TODO: this should return an error and end option parsing
340                  * ... but doing this would change the VLC API and all the
341                  * modules so i'll do it later */
342                 continue;
343             }
344             if( p_conf->psz_oldname
345              && !strcmp( p_conf->psz_oldname, name ) )
346             {
347                  psz_name = p_conf->psz_name;
348                  msg_Warn( p_this, "Option %s is obsolete. Use %s instead.",
349                            name, psz_name );
350             }
351         }
352         /* </Check if the option is deprecated> */
353
354         /* get the type of the variable */
355         i_type = config_GetType( p_this, psz_name );
356         if( !i_type )
357         {
358             msg_Warn( p_this, "unknown option %s (value=%s)",
359                       cfg->psz_name, cfg->psz_value );
360             continue;
361         }
362
363         i_type &= CONFIG_ITEM;
364
365         if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
366         {
367             msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
368             continue;
369         }
370         if( i_type != VLC_VAR_STRING && b_once )
371         {
372             msg_Warn( p_this, "*option_name need to be a string option" );
373             continue;
374         }
375
376         switch( i_type )
377         {
378             case VLC_VAR_BOOL:
379                 val.b_bool = b_yes;
380                 break;
381             case VLC_VAR_INTEGER:
382                 val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
383                                     NULL, 0 );
384                 break;
385             case VLC_VAR_FLOAT:
386                 val.f_float = us_atof( cfg->psz_value ? cfg->psz_value : "0" );
387                 break;
388             case VLC_VAR_STRING:
389             case VLC_VAR_MODULE:
390                 val.psz_string = cfg->psz_value;
391                 break;
392             default:
393                 msg_Warn( p_this, "unhandled config var type (%d)", i_type );
394                 memset( &val, 0, sizeof( vlc_value_t ) );
395                 break;
396         }
397         if( b_once )
398         {
399             vlc_value_t val2;
400
401             var_Get( p_this, psz_name, &val2 );
402             if( *val2.psz_string )
403             {
404                 free( val2.psz_string );
405                 msg_Dbg( p_this, "ignoring option %s (not first occurrence)", psz_name );
406                 continue;
407             }
408             free( val2.psz_string );
409         }
410         var_Set( p_this, psz_name, val );
411         msg_Dbg( p_this, "set config option: %s to %s", psz_name,
412                  cfg->psz_value ? cfg->psz_value : "(null)" );
413     }
414 }
415
416 config_chain_t *config_ChainDuplicate( const config_chain_t *p_src )
417 {
418     config_chain_t *p_dst = NULL;
419     config_chain_t **pp_last = &p_dst;
420
421     for( ; p_src != NULL; p_src = p_src->p_next )
422     {
423         config_chain_t *p = malloc( sizeof(*p) );
424         if( !p )
425             break;
426         p->p_next    = NULL;
427         p->psz_name  = p_src->psz_name  ? strdup( p_src->psz_name )  : NULL;
428         p->psz_value = p_src->psz_value ? strdup( p_src->psz_value ) : NULL;
429
430         *pp_last = p;
431         pp_last = &p->p_next;
432     }
433     return p_dst;
434 }
435
436 char *config_StringUnescape( char *psz_string )
437 {
438     char *psz_src = psz_string;
439     char *psz_dst = psz_string;
440     if( !psz_src )
441         return NULL;
442
443     while( *psz_src )
444     {
445         if( IsEscape( psz_src ) )
446             psz_src++;
447         *psz_dst++ = *psz_src++;
448     }
449     *psz_dst = '\0';
450
451     return psz_string;
452 }
453
454 char *config_StringEscape( const char *psz_string )
455 {
456     char *psz_return;
457     char *psz_dst;
458     int i_escape;
459
460     if( !psz_string )
461         return NULL;
462
463     i_escape = 0;
464     for( const char *p = psz_string; *p; p++ )
465     {
466         if( IsEscapeNeeded( *p ) )
467             i_escape++;
468     }
469
470     psz_return = psz_dst = malloc( strlen( psz_string ) + i_escape + 1 );
471     for( const char *p = psz_string; *p; p++ )
472     {
473         if( IsEscapeNeeded( *p ) )
474             *psz_dst++ = '\\';
475         *psz_dst++ = *p;
476     }
477     *psz_dst = '\0';
478
479     return psz_return;
480 }
481
482