LGPL
[vlc.git] / src / config / chain.c
index 029c255..c824657 100644 (file)
@@ -1,26 +1,26 @@
 /*****************************************************************************
  * 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;
-
-    if( !p ) return NULL;
-
-    SKIPSPACE( p );
-
-    while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;
+    char *psz_value = NULL;
+    const char *end;
+    bool b_keep_brackets = (*p == '{');
 
-    if( p == psz_chain ) return NULL;
-
-    *ppsz_name = strndup( psz_chain, p - psz_chain );
+    if( *p == '=' )
+        p++;
 
-    SKIPSPACE( p );
+    end = ChainGetEnd( p );
+    if( end <= p )
+    {
+        psz_value = NULL;
+    }
+    else
+    {
+        /* Skip heading and trailing spaces.
+         * This ain't necessary but will avoid simple
+         * user mistakes. */
+        SKIPSPACE( p );
+    }
 
-    if( *p == '{' )
+    if( end <= p )
+    {
+        psz_value = NULL;
+    }
+    else
     {
-        const char *psz_name;
+        if( *p == '\'' || *p == '"' || ( !b_keep_brackets && *p == '{' ) )
+        {
+            p++;
 
-        p++;
+            if( *(end-1) != '\'' && *(end-1) == '"' )
+                SKIPTRAILINGSPACE( p, end );
 
-        for( ;; )
+            if( end - 1 <= p )
+                psz_value = NULL;
+            else
+                psz_value = strndup( p, end -1 - p );
+        }
+        else
         {
-            config_chain_t cfg;
-
-            SKIPSPACE( p );
+            SKIPTRAILINGSPACE( p, end );
+            if( end <= p )
+                psz_value = NULL;
+            else
+                psz_value = strndup( p, end - p );
+        }
+    }
 
-            psz_name = p;
+    /* */
+    if( psz_value )
+        config_StringUnescape( psz_value );
 
-            while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
-                   *p != ' ' && *p != '\t' ) p++;
+    /* */
+    *ppsz_string = end;
+    return psz_value;
+}
 
-            /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
-            if( p == psz_name )
-            {
-                fprintf( stderr, "invalid options (empty)" );
-                break;
-            }
+char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg,
+                          const char *psz_chain )
+{
+    config_chain_t **pp_next = pp_cfg;
+    size_t len;
 
-            cfg.psz_name = strndup( psz_name, p - psz_name );
+    *ppsz_name = NULL;
+    *pp_cfg    = NULL;
 
-            SKIPSPACE( p );
+    if( !psz_chain )
+        return NULL;
+    SKIPSPACE( psz_chain );
 
-            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;
-            }
+    /* 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;
 
-            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 ) );
+    /* Parse the parameters */
+    SKIPSPACE( psz_chain );
+    if( *psz_chain == '{' )
+    {
+        /* Parse all name=value[,] elements */
+        do
+        {
+            psz_chain++; /* skip previous delimiter */
+            SKIPSPACE( psz_chain );
 
-                p_cfg = p_cfg->p_next;
-            }
-            else
-            {
-                p_cfg = malloc( sizeof( config_chain_t ) );
-                memcpy( p_cfg, &cfg, sizeof( config_chain_t ) );
+            /* Look for the end of the name (,={}_space_) */
+            len = strcspn( psz_chain, "=,{} \t" );
+            if( len == 0 )
+                continue; /* ignore empty parameter */
 
-                *pp_cfg = p_cfg;
-            }
+            /* Append the new parameter */
+            config_chain_t *p_cfg = malloc( sizeof(*p_cfg) );
+            if( !p_cfg )
+                break;
+            p_cfg->psz_name = strndup( psz_chain, len );
+            psz_chain += len;
+            p_cfg->psz_value = NULL;
+            p_cfg->p_next = NULL;
 
-            if( *p == ',' ) p++;
+            *pp_next = p_cfg;
+            pp_next = &p_cfg->p_next;
 
-            if( *p == '}' )
+            /* Extract the option value */
+            SKIPSPACE( psz_chain );
+            if( strchr( "={", *psz_chain ) )
             {
-                p++;
-                break;
+                p_cfg->psz_value = ChainGetValue( &psz_chain );
+                SKIPSPACE( psz_chain );
             }
         }
+        while( !memchr( "}", *psz_chain, 2 ) );
+
+        if( *psz_chain ) psz_chain++; /* skip '}' */;
+        SKIPSPACE( psz_chain );
     }
 
-    if( *p == ':' ) return( strdup( p + 1 ) );
+    if( *psz_chain == ':' )
+        return strdup( psz_chain + 1 );
 
     return NULL;
 }
@@ -228,8 +256,9 @@ void config_ChainDestroy( config_chain_t *p_cfg )
     }
 }
 
-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 );
@@ -252,8 +281,8 @@ void __config_ChainParse( vlc_object_t *p_this, const char *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;
@@ -272,14 +301,14 @@ void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
                 ( !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;
             }
 
@@ -313,37 +342,6 @@ void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
                  * 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> */
 
@@ -356,8 +354,6 @@ void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
             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 );
@@ -379,10 +375,9 @@ void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
                                     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:
@@ -408,3 +403,62 @@ void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
                  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;
+}