/*****************************************************************************
* chain.c : configuration module chain parsing stuff
*****************************************************************************
- * Copyright (C) 2002-2007 the VideoLAN team
+ * Copyright (C) 2002-2007 VLC authors and VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
# include "config.h"
#endif
-#include <vlc/vlc.h>
+#include <vlc_common.h>
#include "libvlc.h"
+#include <vlc_charset.h>
#include "vlc_interface.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
+static bool IsEscapeNeeded( char c )
+{
+ return c == '\'' || c == '"' || c == '\\';
+}
+static bool IsEscape( const char *psz )
+{
+ if( !psz )
+ return false;
+ return psz[0] == '\\' && IsEscapeNeeded( psz[1] );
+}
+static bool IsSpace( char c )
+{
+ return c == ' ' || c == '\t';
+}
-/* chain format:
- module{option=*:option=*}[:module{option=*:...}]
- */
-#define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
+#define SKIPSPACE( p ) p += strspn( p, " \t" )
#define SKIPTRAILINGSPACE( p, e ) \
- { while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; }
-
-/* go accross " " and { } */
-static const char *_get_chain_end( const char *str )
+ do { while( e > p && IsSpace( *(e-1) ) ) e--; } while(0)
+
+/**
+ * This function will return a pointer after the end of a string element.
+ * It will search the closing element which is
+ * } for { (it will handle nested { ... })
+ * " for "
+ * ' for '
+ */
+static const char *ChainGetEnd( const char *psz_string )
{
+ const char *p = psz_string;
char c;
- const char *p = str;
+ if( !psz_string )
+ return NULL;
+
+ /* Look for a opening character */
SKIPSPACE( p );
- for( ;; )
+ for( ;; p++)
{
- if( !*p || *p == ',' || *p == '}' ) return p;
+ if( *p == '\0' || *p == ',' || *p == '}' )
+ return p;
- if( *p != '{' && *p != '"' && *p != '\'' )
- {
- p++;
- continue;
- }
-
- if( *p == '{' ) c = '}';
- else c = *p;
- p++;
+ if( *p == '{' || *p == '"' || *p == '\'' )
+ break;
+ }
- for( ;; )
- {
- if( !*p ) return p;
+ /* Set c to the closing character */
+ if( *p == '{' )
+ c = '}';
+ else
+ c = *p;
+ p++;
- if( *p == c ) return ++p;
- else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
- else p++;
- }
+ /* Search the closing character, handle nested {..} */
+ for( ;; )
+ {
+ if( *p == '\0')
+ return p;
+
+ if( IsEscape( p ) )
+ p += 2;
+ else if( *p == c )
+ return ++p;
+ else if( *p == '{' && c == '}' )
+ p = ChainGetEnd( p );
+ else
+ p++;
}
}
-char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg, const char *psz_chain )
+/**
+ * It will extract an option value (=... or {...}).
+ * It will remove the initial = if present but keep the {}
+ */
+static char *ChainGetValue( const char **ppsz_string )
{
- config_chain_t *p_cfg = NULL;
- const char *p = psz_chain;
+ const char *p = *ppsz_string;
- *ppsz_name = NULL;
- *pp_cfg = NULL;
+ char *psz_value = NULL;
+ const char *end;
+ bool b_keep_brackets = (*p == '{');
- if( !p ) return NULL;
-
- SKIPSPACE( p );
-
- while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;
-
- if( p == psz_chain ) return NULL;
-
- *ppsz_name = strndup( psz_chain, p - psz_chain );
-
- SKIPSPACE( p );
+ if( *p == '=' )
+ p++;
- if( *p == '{' )
+ end = ChainGetEnd( p );
+ if( end <= p )
{
- const char *psz_name;
-
- p++;
+ psz_value = NULL;
+ }
+ else
+ {
+ /* Skip heading and trailing spaces.
+ * This ain't necessary but will avoid simple
+ * user mistakes. */
+ SKIPSPACE( p );
+ }
- for( ;; )
+ if( end <= p )
+ {
+ psz_value = NULL;
+ }
+ else
+ {
+ if( *p == '\'' || *p == '"' || ( !b_keep_brackets && *p == '{' ) )
{
- config_chain_t cfg;
+ p++;
- SKIPSPACE( p );
+ if( *(end-1) != '\'' && *(end-1) == '"' )
+ SKIPTRAILINGSPACE( p, end );
- psz_name = p;
+ if( end - 1 <= p )
+ psz_value = NULL;
+ else
+ psz_value = strndup( p, end -1 - p );
+ }
+ else
+ {
+ SKIPTRAILINGSPACE( p, end );
+ if( end <= p )
+ psz_value = NULL;
+ else
+ psz_value = strndup( p, end - p );
+ }
+ }
- while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
- *p != ' ' && *p != '\t' ) p++;
+ /* */
+ if( psz_value )
+ config_StringUnescape( psz_value );
- /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
- if( p == psz_name )
- {
- fprintf( stderr, "invalid options (empty)" );
- break;
- }
+ /* */
+ *ppsz_string = end;
+ return psz_value;
+}
- cfg.psz_name = strndup( psz_name, p - psz_name );
+/* Parse all name=value[,] elements */
+const char *config_ChainParseOptions( config_chain_t **pp_cfg, const char *psz_opts )
+{
+ config_chain_t **pp_next = pp_cfg;
+ bool first = true;
+ do
+ {
+ if (!first)
+ psz_opts++; /* skip previous delimiter */
+ SKIPSPACE( psz_opts );
+
+ first = false;
+
+ /* Look for the end of the name (,={}_space_) */
+ size_t len = strcspn( psz_opts, "=,{} \t" );
+ if( len == 0 )
+ continue; /* ignore empty parameter */
+
+ /* Append the new parameter */
+ config_chain_t *p_cfg = malloc( sizeof(*p_cfg) );
+ if( !p_cfg )
+ break;
+ p_cfg->psz_name = strndup( psz_opts, len );
+ psz_opts += len;
+ p_cfg->psz_value = NULL;
+ p_cfg->p_next = NULL;
+
+ *pp_next = p_cfg;
+ pp_next = &p_cfg->p_next;
+
+ /* Extract the option value */
+ SKIPSPACE( psz_opts );
+ if( strchr( "={", *psz_opts ) )
+ {
+ p_cfg->psz_value = ChainGetValue( &psz_opts );
+ SKIPSPACE( psz_opts );
+ }
+ }
+ while( !memchr( "}", *psz_opts, 2 ) );
- SKIPSPACE( p );
+ if( *psz_opts ) psz_opts++; /* skip '}' */;
+ SKIPSPACE( psz_opts );
- if( *p == '=' || *p == '{' )
- {
- const char *end;
- vlc_bool_t b_keep_brackets = (*p == '{');
-
- if( *p == '=' ) p++;
-
- end = _get_chain_end( p );
- if( end <= p )
- {
- cfg.psz_value = NULL;
- }
- else
- {
- /* Skip heading and trailing spaces.
- * This ain't necessary but will avoid simple
- * user mistakes. */
- SKIPSPACE( p );
- }
-
- if( end <= p )
- {
- cfg.psz_value = NULL;
- }
- else
- {
- if( *p == '\'' || *p == '"' ||
- ( !b_keep_brackets && *p == '{' ) )
- {
- p++;
-
- if( *(end-1) != '\'' && *(end-1) == '"' )
- SKIPTRAILINGSPACE( p, end );
-
- if( end - 1 <= p ) cfg.psz_value = NULL;
- else cfg.psz_value = strndup( p, end -1 - p );
- }
- else
- {
- SKIPTRAILINGSPACE( p, end );
- if( end <= p ) cfg.psz_value = NULL;
- else cfg.psz_value = strndup( p, end - p );
- }
- }
-
- p = end;
- SKIPSPACE( p );
- }
- else
- {
- cfg.psz_value = NULL;
- }
+ return psz_opts;
+}
- cfg.p_next = NULL;
- if( p_cfg )
- {
- p_cfg->p_next = malloc( sizeof( config_chain_t ) );
- memcpy( p_cfg->p_next, &cfg, sizeof( config_chain_t ) );
+char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg,
+ const char *psz_chain )
+{
+ size_t len;
- p_cfg = p_cfg->p_next;
- }
- else
- {
- p_cfg = malloc( sizeof( config_chain_t ) );
- memcpy( p_cfg, &cfg, sizeof( config_chain_t ) );
+ *ppsz_name = NULL;
+ *pp_cfg = NULL;
- *pp_cfg = p_cfg;
- }
+ if( !psz_chain )
+ return NULL;
+ SKIPSPACE( psz_chain );
- if( *p == ',' ) p++;
+ /* Look for parameter (a {...} or :...) or end of name (space or nul) */
+ len = strcspn( psz_chain, "{: \t" );
+ *ppsz_name = strndup( psz_chain, len );
+ psz_chain += len;
- if( *p == '}' )
- {
- p++;
- break;
- }
- }
- }
+ /* Parse the parameters */
+ SKIPSPACE( psz_chain );
+ if( *psz_chain == '{' )
+ psz_chain = config_ChainParseOptions( pp_cfg, psz_chain );
- if( *p == ':' ) return( strdup( p + 1 ) );
+ if( *psz_chain == ':' )
+ return strdup( psz_chain + 1 );
return NULL;
}
}
}
-void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
- const char *const *ppsz_options, config_chain_t *cfg )
+#undef config_ChainParse
+void config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
+ const char *const *ppsz_options, config_chain_t *cfg )
{
if( psz_prefix == NULL ) psz_prefix = "";
size_t plen = 1 + strlen( psz_prefix );
for(; cfg; cfg = cfg->p_next )
{
vlc_value_t val;
- vlc_bool_t b_yes = VLC_TRUE;
- vlc_bool_t b_once = VLC_FALSE;
+ bool b_yes = true;
+ bool b_once = false;
module_config_t *p_conf;
int i_type;
size_t i;
( !strncmp( cfg->psz_name, "no", 2 ) &&
!strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
{
- b_yes = VLC_FALSE;
+ b_yes = false;
break;
}
if( *ppsz_options[i] == '*' &&
!strcmp( &ppsz_options[i][1], cfg->psz_name ) )
{
- b_once = VLC_TRUE;
+ b_once = true;
break;
}
* modules so i'll do it later */
continue;
}
- if( p_conf->psz_oldname
- && !strcmp( p_conf->psz_oldname, name ) )
- {
- psz_name = p_conf->psz_name;
- msg_Warn( p_this, "Option %s is obsolete. Use %s instead.",
- name, psz_name );
- }
- if( p_conf->b_safe )
- {
- int policy = config_GetInt( p_this, "security-policy" );
- switch( policy )
- {
- case 0: /* block */
- msg_Err( p_this, "option %s is unsafe and is blocked by security policy", psz_name );
- return;
- case 1: /* allow */
- break;
- case 2: /* prompt */
- {
- char description[256];
- snprintf(description, sizeof(description), _("playlist item is making use of the following unsafe option '%s', which may be harmful if used in a malicious way, authorize it ?"), psz_name);
- if( DIALOG_OK_YES != intf_UserYesNo( p_this, _("WARNING: Unsafe Playlist"), description, _("Yes"), _("No"), NULL) )
- {
- msg_Err( p_this, "option %s is unsafe and is blocked by security policy", psz_name );
- return;
- }
- }
- default:
- ;
- }
- }
}
/* </Check if the option is deprecated> */
continue;
}
- i_type &= CONFIG_ITEM;
-
if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
{
msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
NULL, 0 );
break;
case VLC_VAR_FLOAT:
- val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
+ val.f_float = us_atof( cfg->psz_value ? cfg->psz_value : "0" );
break;
case VLC_VAR_STRING:
- case VLC_VAR_MODULE:
val.psz_string = cfg->psz_value;
break;
default:
cfg->psz_value ? cfg->psz_value : "(null)" );
}
}
+
+config_chain_t *config_ChainDuplicate( const config_chain_t *p_src )
+{
+ config_chain_t *p_dst = NULL;
+ config_chain_t **pp_last = &p_dst;
+
+ for( ; p_src != NULL; p_src = p_src->p_next )
+ {
+ config_chain_t *p = malloc( sizeof(*p) );
+ if( !p )
+ break;
+ p->p_next = NULL;
+ p->psz_name = p_src->psz_name ? strdup( p_src->psz_name ) : NULL;
+ p->psz_value = p_src->psz_value ? strdup( p_src->psz_value ) : NULL;
+
+ *pp_last = p;
+ pp_last = &p->p_next;
+ }
+ return p_dst;
+}
+
+char *config_StringUnescape( char *psz_string )
+{
+ char *psz_src = psz_string;
+ char *psz_dst = psz_string;
+ if( !psz_src )
+ return NULL;
+
+ while( *psz_src )
+ {
+ if( IsEscape( psz_src ) )
+ psz_src++;
+ *psz_dst++ = *psz_src++;
+ }
+ *psz_dst = '\0';
+
+ return psz_string;
+}
+
+char *config_StringEscape( const char *str )
+{
+ size_t length = 0;
+
+ if( str == NULL )
+ return NULL;
+
+ for( const char *p = str; *p; p++ )
+ length += IsEscapeNeeded( *p ) ? 2 : 1;
+
+ char *ret = xmalloc( length + 1 ), *dst = ret;
+ for( const char *p = str; *p; p++ )
+ {
+ if( IsEscapeNeeded( *p ) )
+ *dst++ = '\\';
+ *dst++ = *p;
+ }
+ *dst = '\0';;
+ return ret;
+}