]> git.sesse.net Git - vlc/blobdiff - src/config/file.c
Qt: use the theme icons for the system tray too
[vlc] / src / config / file.c
index 1f99e359ebed4cffc08200639b0583c4d1e264e7..1c0b876883d138e8e084b5f82ffe8a4ded305bcb 100644 (file)
@@ -1,49 +1,55 @@
 /*****************************************************************************
  * file.c: configuration file handling
  *****************************************************************************
- * Copyright (C) 2001-2007 the VideoLAN team
+ * Copyright (C) 2001-2007 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Gildas Bazin <gbazin@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.
  *****************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 
-#include <vlc_common.h>
-#include "../libvlc.h"
-#include "vlc_charset.h"
-#include "vlc_keys.h"
-
 #include <errno.h>                                                  /* errno */
 #include <assert.h>
 #include <limits.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 #ifdef __APPLE__
 #   include <xlocale.h>
-#else
+#elif defined(HAVE_USELOCALE)
 #include <locale.h>
 #endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <vlc_common.h>
+#include "../libvlc.h"
+#include <vlc_charset.h>
+#include <vlc_fs.h>
+#include <vlc_keys.h>
+#include <vlc_modules.h>
+#include <vlc_plugin.h>
 
 #include "configuration.h"
 #include "modules/modules.h"
 
-static char *ConfigKeyToString( int );
-
 static inline char *strdupnull (const char *src)
 {
     return src ? strdup (src) : NULL;
@@ -54,10 +60,11 @@ static inline char *strdupnull (const char *src)
  */
 static char *config_GetConfigFile( vlc_object_t *obj )
 {
-    char *psz_file = config_GetPsz( obj, "config" );
+    char *psz_file = var_CreateGetNonEmptyString( obj, "config" );
+    var_Destroy( obj, "config" );
     if( psz_file == NULL )
     {
-        char *psz_dir = config_GetUserConfDir();
+        char *psz_dir = config_GetUserDir( VLC_CONFIG_DIR );
 
         if( asprintf( &psz_file, "%s" DIR_SEP CONFIG_FILE, psz_dir ) == -1 )
             psz_file = NULL;
@@ -66,7 +73,7 @@ static char *config_GetConfigFile( vlc_object_t *obj )
     return psz_file;
 }
 
-static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode )
+static FILE *config_OpenConfigFile( vlc_object_t *p_obj )
 {
     char *psz_filename = config_GetConfigFile( p_obj );
     if( psz_filename == NULL )
@@ -74,23 +81,26 @@ static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode )
 
     msg_Dbg( p_obj, "opening config file (%s)", psz_filename );
 
-    FILE *p_stream = utf8_fopen( psz_filename, mode );
+    FILE *p_stream = vlc_fopen( psz_filename, "rt" );
     if( p_stream == NULL && errno != ENOENT )
     {
         msg_Err( p_obj, "cannot open config file (%s): %m",
                  psz_filename );
 
     }
-#if !( defined(WIN32) || defined(__APPLE__) || defined(SYS_BEOS) )
-    else if( p_stream == NULL && errno == ENOENT && mode[0] == 'r' )
+#if !( defined(_WIN32) || defined(__APPLE__) || defined(__OS2__) )
+    else if( p_stream == NULL && errno == ENOENT )
     {
         /* This is the fallback for pre XDG Base Directory
          * Specification configs */
+        char *home = config_GetUserDir(VLC_HOME_DIR);
         char *psz_old;
-        if( asprintf( &psz_old, "%s" DIR_SEP CONFIG_DIR DIR_SEP CONFIG_FILE,
-                      config_GetHomeDir() ) != -1 )
+
+        if( home != NULL
+         && asprintf( &psz_old, "%s/.vlc/" CONFIG_FILE,
+                      home ) != -1 )
         {
-            p_stream = utf8_fopen( psz_old, mode );
+            p_stream = vlc_fopen( psz_old, "rt" );
             if( p_stream )
             {
                 /* Old config file found. We want to write it at the
@@ -98,10 +108,10 @@ static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode )
                 msg_Info( p_obj->p_libvlc, "Found old config file at %s. "
                           "VLC will now use %s.", psz_old, psz_filename );
                 char *psz_readme;
-                if( asprintf(&psz_readme,"%s"DIR_SEP CONFIG_DIR DIR_SEP"README",
-                              config_GetHomeDir() ) != -1 )
+                if( asprintf(&psz_readme,"%s/.vlc/README",
+                             home ) != -1 )
                 {
-                    FILE *p_readme = utf8_fopen( psz_readme, "wt" );
+                    FILE *p_readme = vlc_fopen( psz_readme, "wt" );
                     if( p_readme )
                     {
                         fprintf( p_readme, "The VLC media player "
@@ -115,55 +125,57 @@ static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode )
                     }
                     free( psz_readme );
                 }
+                /* Remove the old configuration file so that --reset-config
+                 * can work properly. Fortunately, Linux allows removing
+                 * open files - with most filesystems. */
+                unlink( psz_old );
             }
             free( psz_old );
         }
+        free( home );
     }
 #endif
+    free( psz_filename );
     return p_stream;
 }
 
 
-static int strtoi (const char *str)
+static int64_t strtoi (const char *str)
 {
     char *end;
-    long l;
+    long long l;
 
     errno = 0;
-    l = strtol (str, &end, 0);
+    l = strtoll (str, &end, 0);
 
     if (!errno)
     {
-        if ((l > INT_MAX) || (l < INT_MIN))
+#if (LLONG_MAX > 0x7fffffffffffffffLL)
+        if (l > 0x7fffffffffffffffLL
+         || l < -0x8000000000000000LL)
             errno = ERANGE;
+#endif
         if (*end)
             errno = EINVAL;
     }
-    return (int)l;
+    return l;
 }
 
-
+#undef config_LoadConfigFile
 /*****************************************************************************
  * config_LoadConfigFile: loads the configuration file.
  *****************************************************************************
  * This function is called to load the config options stored in the config
  * file.
  *****************************************************************************/
-int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name )
+int config_LoadConfigFile( vlc_object_t *p_this )
 {
-    libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
     FILE *file;
 
-    file = config_OpenConfigFile (p_this, "rt");
+    file = config_OpenConfigFile (p_this);
     if (file == NULL)
         return VLC_EGENERIC;
 
-    /* Acquire config file lock */
-    vlc_mutex_lock( &priv->config_lock );
-
-    /* Look for the selected module, if NULL then save everything */
-    module_t **list = module_list_get (NULL);
-
     /* Look for UTF-8 Byte Order Mark */
     char * (*convert) (const char *) = strdupnull;
     char bom[3];
@@ -175,133 +187,69 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name )
         rewind (file); /* no BOM, rewind */
     }
 
-    module_t *module = NULL;
-    char line[1024], section[1022];
-    section[0] = '\0';
+    char *line = NULL;
+    size_t bufsize;
+    ssize_t linelen;
 
     /* Ensure consistent number formatting... */
     locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
     locale_t baseloc = uselocale (loc);
 
-    while (fgets (line, 1024, file) != NULL)
+    vlc_rwlock_wrlock (&config_lock);
+    while ((linelen = getline (&line, &bufsize, file)) != -1)
     {
-        /* Ignore comments and empty lines */
-        switch (line[0])
-        {
-            case '#':
-            case '\n':
-            case '\0':
-                continue;
-        }
-
-        if (line[0] == '[')
-        {
-            char *ptr = strchr (line, ']');
-            if (ptr == NULL)
-                continue; /* syntax error; */
-            *ptr = '\0';
-
-            /* New section ( = a given module) */
-            strcpy (section, line + 1);
-            module = NULL;
-
-            if ((psz_module_name == NULL)
-             || (strcmp (psz_module_name, section) == 0))
-            {
-                for (int i = 0; list[i]; i++)
-                {
-                    module_t *m = list[i];
-
-                    if ((strcmp (section, m->psz_object_name) == 0)
-                     && (m->i_config_items > 0)) /* ignore config-less modules */
-                    {
-                        module = m;
-                        if (psz_module_name != NULL)
-                            msg_Dbg (p_this,
-                                     "loading config for module \"%s\"",
-                                     section);
-                        break;
-                    }
-                }
-            }
+        line[linelen - 1] = '\0'; /* trim newline */
 
+        /* Ignore comments, section and empty lines */
+        if (memchr ("#[", line[0], 3) != NULL)
             continue;
-        }
-
-        if (module == NULL)
-            continue; /* no need to parse if there is no matching module */
-
-        char *ptr = strchr (line, '\n');
-        if (ptr != NULL)
-            *ptr = '\0';
 
         /* look for option name */
         const char *psz_option_name = line;
 
-        ptr = strchr (line, '=');
+        char *ptr = strchr (line, '=');
         if (ptr == NULL)
             continue; /* syntax error */
-
         *ptr = '\0';
-        const char *psz_option_value = ptr + 1;
-
-        /* try to match this option with one of the module's options */
-        for (size_t i = 0; i < module->confsize; i++)
-        {
-            module_config_t *p_item = module->p_config + i;
 
-            if ((p_item->i_type & CONFIG_HINT)
-             || strcmp (p_item->psz_name, psz_option_name))
-                continue;
-
-            /* We found it */
-            errno = 0;
+        module_config_t *item = config_FindConfig (p_this, psz_option_name);
+        if (item == NULL)
+            continue;
 
-            switch( p_item->i_type )
+        const char *psz_option_value = ptr + 1;
+        switch (CONFIG_CLASS(item->i_type))
+        {
+            case CONFIG_ITEM_BOOL:
+            case CONFIG_ITEM_INTEGER:
             {
-                case CONFIG_ITEM_BOOL:
-                case CONFIG_ITEM_INTEGER:
-                {
-                    long l = strtoi (psz_option_value);
-                    if (errno)
-                        msg_Warn (p_this, "Integer value (%s) for %s: %m",
-                                  psz_option_value, psz_option_name);
-                    else
-                        p_item->saved.i = p_item->value.i = (int)l;
-                    break;
-                }
-
-                case CONFIG_ITEM_FLOAT:
-                    if( !*psz_option_value )
-                        break;                    /* ignore empty option */
-                    p_item->value.f = (float)atof (psz_option_value);
-                    p_item->saved.f = p_item->value.f;
-                    break;
-
-                case CONFIG_ITEM_KEY:
-                    if( !*psz_option_value )
-                        break;                    /* ignore empty option */
-                    p_item->value.i = ConfigStringToKey(psz_option_value);
-                    p_item->saved.i = p_item->value.i;
-                    break;
-
-                default:
-                    vlc_mutex_lock( p_item->p_lock );
-
-                    /* free old string */
-                    free( (char*) p_item->value.psz );
-                    free( (char*) p_item->saved.psz );
-
-                    p_item->value.psz = convert (psz_option_value);
-                    p_item->saved.psz = strdupnull (p_item->value.psz);
-
-                    vlc_mutex_unlock( p_item->p_lock );
-                    break;
+                int64_t l;
+
+                errno = 0;
+                l = strtoi (psz_option_value);
+                if ((l > item->max.i) || (l < item->min.i))
+                    errno = ERANGE;
+                if (errno)
+                    msg_Warn (p_this, "Integer value (%s) for %s: %m",
+                              psz_option_value, psz_option_name);
+                else
+                    item->value.i = l;
+                break;
             }
 
-            break;
+            case CONFIG_ITEM_FLOAT:
+                if (!*psz_option_value)
+                    break;                    /* ignore empty option */
+                item->value.f = (float)atof (psz_option_value);
+                break;
+
+            default:
+                free ((char *)item->value.psz);
+                item->value.psz = convert (psz_option_value);
+                break;
         }
     }
+    vlc_rwlock_unlock (&config_lock);
+    free (line);
 
     if (ferror (file))
     {
@@ -310,14 +258,11 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name )
     }
     fclose (file);
 
-    module_list_free (list);
     if (loc != (locale_t)0)
     {
         uselocale (baseloc);
         freelocale (loc);
     }
-
-    vlc_mutex_unlock( &priv->config_lock );
     return 0;
 }
 
@@ -328,7 +273,7 @@ int config_CreateDir( vlc_object_t *p_this, const char *psz_dirname )
 {
     if( !psz_dirname || !*psz_dirname ) return -1;
 
-    if( utf8_mkdir( psz_dirname, 0700 ) == 0 )
+    if( vlc_mkdir( psz_dirname, 0700 ) == 0 )
         return 0;
 
     switch( errno )
@@ -348,19 +293,19 @@ int config_CreateDir( vlc_object_t *p_this, const char *psz_dirname )
                 *psz_end = '\0';
                 if( config_CreateDir( p_this, psz_parent ) == 0 )
                 {
-                    if( !utf8_mkdir( psz_dirname, 0700 ) )
+                    if( !vlc_mkdir( psz_dirname, 0700 ) )
                         return 0;
                 }
             }
         }
     }
 
-    msg_Err( p_this, "could not create %s: %m", psz_dirname );
+    msg_Warn( p_this, "could not create %s: %m", psz_dirname );
     return -1;
 }
 
 static int
-config_Write (FILE *file, const char *type, const char *desc,
+config_Write (FILE *file, const char *desc, const char *type,
               bool comment, const char *name, const char *fmt, ...)
 {
     va_list ap;
@@ -387,8 +332,8 @@ config_Write (FILE *file, const char *type, const char *desc,
 
 static int config_PrepareDir (vlc_object_t *obj)
 {
-    char *psz_configdir = config_GetUserConfDir ();
-    if (psz_configdir == NULL) /* XXX: This should never happen */
+    char *psz_configdir = config_GetUserDir (VLC_CONFIG_DIR);
+    if (psz_configdir == NULL)
         return -1;
 
     int ret = config_CreateDir (obj, psz_configdir);
@@ -396,162 +341,101 @@ static int config_PrepareDir (vlc_object_t *obj)
     return ret;
 }
 
-/*****************************************************************************
- * config_SaveConfigFile: Save a module's config options.
- *****************************************************************************
- * This will save the specified module's config options to the config file.
- * If psz_module_name is NULL then we save all the modules config options.
- * It's no use to save the config options that kept their default values, so
- * we'll try to be a bit clever here.
- *
- * When we save we mustn't delete the config options of the modules that
- * haven't been loaded. So we cannot just create a new config file with the
- * config structures we've got in memory.
- * I don't really know how to deal with this nicely, so I will use a completly
- * dumb method ;-)
- * I will load the config file in memory, but skipping all the sections of the
- * modules we want to save. Then I will create a brand new file, dump the file
- * loaded in memory and then append the sections of the modules we want to
- * save.
- * Really stupid no ?
- *****************************************************************************/
-static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
-                           bool b_autosave )
+#undef config_SaveConfigFile
+/**
+ * Saves the in-memory configuration into a file.
+ * @return 0 on success, -1 on error.
+ */
+int config_SaveConfigFile (vlc_object_t *p_this)
 {
-    libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
-    module_t *p_parser;
-    FILE *file;
-    char p_line[1024], *p_index2;
-    unsigned long i_sizebuf = 0;
-    char *p_bigbuffer, *p_index;
-    bool b_backup;
-    int i_index;
-
-    /* Acquire config file lock */
-    vlc_mutex_lock( &priv->config_lock );
 
     if( config_PrepareDir( p_this ) )
     {
         msg_Err( p_this, "no configuration directory" );
-        vlc_mutex_unlock( &priv->config_lock );
         return -1;
     }
 
-    file = config_OpenConfigFile( p_this, "rt" );
-    if( file != NULL )
-    {
-        /* look for file size */
-        fseek( file, 0L, SEEK_END );
-        i_sizebuf = ftell( file );
-        fseek( file, 0L, SEEK_SET );
-        if( i_sizebuf >= LONG_MAX )
-            i_sizebuf = 0;
-    }
-
-    p_bigbuffer = p_index = malloc( i_sizebuf+1 );
-    if( !p_bigbuffer )
+    /*
+     * Save module config in file
+     */
+    char *temporary;
+    char *permanent = config_GetConfigFile (p_this);
+    if (permanent == NULL)
+        return -1;
+    if (asprintf (&temporary, "%s.%u", permanent, getpid ()) == -1)
     {
-        if( file ) fclose( file );
-        vlc_mutex_unlock( &priv->config_lock );
+        free (permanent);
         return -1;
     }
-    p_bigbuffer[0] = 0;
-
-    /* List all available modules */
-    module_t **list = module_list_get (NULL);
-
-    /* backup file into memory, we only need to backup the sections we won't
-     * save later on */
-    b_backup = false;
-    while( file && fgets( p_line, 1024, file ) )
+    else
     {
-        if( (p_line[0] == '[') && (p_index2 = strchr(p_line,']')))
-        {
-
-            /* we found a section, check if we need to do a backup */
-            for( i_index = 0; (p_parser = list[i_index]) != NULL; i_index++ )
-            {
-                if( ((p_index2 - &p_line[1])
-                       == (int)strlen(p_parser->psz_object_name) )
-                    && !memcmp( &p_line[1], p_parser->psz_object_name,
-                                strlen(p_parser->psz_object_name) ) )
-                {
-                    if( !psz_module_name )
-                        break;
-                    else if( !strcmp( psz_module_name,
-                                      p_parser->psz_object_name ) )
-                        break;
-                }
-            }
-
-            if( list[i_index] == NULL )
-            {
-                /* we don't have this section in our list so we need to back
-                 * it up */
-                *p_index2 = 0;
-#if 0
-                msg_Dbg( p_this, "backing up config for unknown module \"%s\"",
-                                 &p_line[1] );
-#endif
-                *p_index2 = ']';
-
-                b_backup = true;
-            }
-            else
-            {
-                b_backup = false;
-            }
-        }
+        struct stat st;
 
-        /* save line if requested and line is valid (doesn't begin with a
-         * space, tab, or eol) */
-        if( b_backup && (p_line[0] != '\n') && (p_line[0] != ' ')
-            && (p_line[0] != '\t') )
+        /* Some users make vlcrc read-only to prevent changes.
+         * The atomic replacement scheme breaks this "feature",
+         * so we check for read-only by hand. */
+        if (stat (permanent, &st) == 0 && !(st.st_mode & S_IWUSR))
         {
-            strcpy( p_index, p_line );
-            p_index += strlen( p_line );
+            msg_Err (p_this, "configuration file is read-only");
+            goto error;
         }
     }
-    if( file ) fclose( file );
 
+    /* Configuration lock must be taken before vlcrc serializer below. */
+    vlc_rwlock_rdlock (&config_lock);
 
-    /*
-     * Save module config in file
-     */
+    /* The temporary configuration file is per-PID. Therefore this function
+     * should be serialized against itself within a given process. */
+    static vlc_mutex_t lock = VLC_STATIC_MUTEX;
+    vlc_mutex_lock (&lock);
 
-    file = config_OpenConfigFile (p_this, "wt");
-    if( !file )
+    int fd = vlc_open (temporary, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
+    if (fd == -1)
     {
-        module_list_free (list);
-        free( p_bigbuffer );
-        vlc_mutex_unlock( &priv->config_lock );
-        return -1;
+        vlc_rwlock_unlock (&config_lock);
+        vlc_mutex_unlock (&lock);
+        goto error;
+    }
+    FILE *file = fdopen (fd, "wt");
+    if (file == NULL)
+    {
+        msg_Err (p_this, "cannot create configuration file: %m");
+        vlc_rwlock_unlock (&config_lock);
+        close (fd);
+        vlc_mutex_unlock (&lock);
+        goto error;
     }
 
-    fprintf( file, "\xEF\xBB\xBF###\n###  " COPYRIGHT_MESSAGE "\n###\n\n"
-       "###\n### lines beginning with a '#' character are comments\n###\n\n" );
+    fprintf( file,
+        "\xEF\xBB\xBF###\n"
+        "###  "PACKAGE_NAME" "PACKAGE_VERSION"\n"
+        "###\n"
+        "\n"
+        "###\n"
+        "### lines beginning with a '#' character are comments\n"
+        "###\n"
+        "\n" );
 
     /* Ensure consistent number formatting... */
     locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
     locale_t baseloc = uselocale (loc);
 
+    /* We would take the config lock here. But this would cause a lock
+     * inversion with the serializer above and config_AutoSaveConfigFile().
+    vlc_rwlock_rdlock (&config_lock);*/
+
     /* Look for the selected module, if NULL then save everything */
-    for( i_index = 0; (p_parser = list[i_index]) != NULL; i_index++ )
+    size_t count;
+    module_t **list = module_list_get (&count);
+    for (size_t i = 0; i < count; i++)
     {
+        module_t *p_parser = list[i];
         module_config_t *p_item, *p_end;
 
-        if( psz_module_name && strcmp( psz_module_name,
-                                       p_parser->psz_object_name ) )
-            continue;
-
         if( !p_parser->i_config_items )
             continue;
 
-        if( psz_module_name )
-            msg_Dbg( p_this, "saving config for module \"%s\"",
-                     p_parser->psz_object_name );
-
-        fprintf( file, "[%s]", p_parser->psz_object_name );
+        fprintf( file, "[%s]", module_get_object (p_parser) );
         if( p_parser->psz_longname )
             fprintf( file, " # %s\n\n", p_parser->psz_longname );
         else
@@ -561,83 +445,44 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
              p_item < p_end;
              p_item++ )
         {
-            /* Do not save the new value in the configuration file
-             * if doing an autosave, and the item is not an "autosaved" one. */
-            bool b_retain = b_autosave && !p_item->b_autosave;
-
-            if ((p_item->i_type & CONFIG_HINT) /* ignore hint */
+            if (!CONFIG_ITEM(p_item->i_type)   /* ignore hint */
              || p_item->b_removed              /* ignore deprecated option */
              || p_item->b_unsaveable)          /* ignore volatile option */
                 continue;
 
             if (IsConfigIntegerType (p_item->i_type))
             {
-                int val = b_retain ? p_item->saved.i : p_item->value.i;
-                if (p_item->i_type == CONFIG_ITEM_KEY)
-                {
-                    char *psz_key = ConfigKeyToString (val);
-                    config_Write (file, p_item->psz_text, N_("key"),
-                                  val == p_item->orig.i,
-                                  p_item->psz_name, "%s",
-                                  psz_key ? psz_key : "");
-                    free (psz_key);
-                }
-                else
-                    config_Write (file, p_item->psz_text,
-                                  (p_item->i_type == CONFIG_ITEM_BOOL)
-                                      ? N_("boolean") : N_("integer"),
-                                  val == p_item->orig.i,
-                                  p_item->psz_name, "%d", val);
-                p_item->saved.i = val;
+                int64_t val = p_item->value.i;
+                config_Write (file, p_item->psz_text,
+                             (CONFIG_CLASS(p_item->i_type) == CONFIG_ITEM_BOOL)
+                                  ? N_("boolean") : N_("integer"),
+                              val == p_item->orig.i,
+                              p_item->psz_name, "%"PRId64, val);
             }
             else
             if (IsConfigFloatType (p_item->i_type))
             {
-                float val = b_retain ? p_item->saved.f : p_item->value.f;
+                float val = p_item->value.f;
                 config_Write (file, p_item->psz_text, N_("float"),
                               val == p_item->orig.f,
                               p_item->psz_name, "%f", val);
-                p_item->saved.f = val;
             }
             else
             {
-                const char *psz_value = b_retain ? p_item->saved.psz
-                                                 : p_item->value.psz;
+                const char *psz_value = p_item->value.psz;
                 bool modified;
 
                 assert (IsConfigStringType (p_item->i_type));
 
-                if (b_retain && (psz_value == NULL)) /* FIXME: hack */
-                    psz_value = p_item->orig.psz;
-
-                modified =
-                    (psz_value != NULL)
-                        ? ((p_item->orig.psz != NULL)
-                            ? (strcmp (psz_value, p_item->orig.psz) != 0)
-                            : true)
-                        : (p_item->orig.psz != NULL);
-
+                modified = !!strcmp (psz_value ? psz_value : "",
+                                     p_item->orig.psz ? p_item->orig.psz : "");
                 config_Write (file, p_item->psz_text, N_("string"),
                               !modified, p_item->psz_name, "%s",
                               psz_value ? psz_value : "");
-
-                if ( !b_retain )
-                {
-
-                    free ((char *)p_item->saved.psz);
-                    if( (psz_value && p_item->orig.psz &&
-                         strcmp( psz_value, p_item->orig.psz )) ||
-                        !psz_value || !p_item->orig.psz)
-                        p_item->saved.psz = strdupnull (psz_value);
-                    else
-                        p_item->saved.psz = NULL;
-                }
             }
-
-            if (!b_retain)
-                p_item->b_dirty = false;
         }
     }
+    vlc_rwlock_unlock (&config_lock);
 
     module_list_free (list);
     if (loc != (locale_t)0)
@@ -647,114 +492,61 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
     }
 
     /*
-     * Restore old settings from the config in file
+     * Flush to disk and replace atomically
      */
-    fputs( p_bigbuffer, file );
-    free( p_bigbuffer );
-
-    fclose( file );
-    vlc_mutex_unlock( &priv->config_lock );
-
-    return 0;
-}
-
-int config_AutoSaveConfigFile( vlc_object_t *p_this )
-{
-    libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
-    size_t i_index;
-    bool done;
-
-    assert( p_this );
-
-    /* Check if there's anything to save */
-    vlc_mutex_lock( &priv->config_lock );
-    module_t **list = module_list_get (NULL);
-    for( i_index = 0; list[i_index]; i_index++ )
+    fflush (file); /* Flush from run-time */
+    if (ferror (file))
     {
-        module_t *p_parser = list[i_index];
-        module_config_t *p_item, *p_end;
-
-        if( !p_parser->i_config_items ) continue;
-
-        for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize;
-             p_item < p_end;
-             p_item++ )
-        {
-            if( p_item->b_autosave && p_item->b_dirty ) break;
-        }
-        if( p_item < p_end ) break;
+        vlc_unlink (temporary);
+        vlc_mutex_unlock (&lock);
+        msg_Err (p_this, "cannot write configuration file");
+        fclose (file);
+        goto error;
     }
-    done = list[i_index] == NULL;
-    module_list_free (list);
-    vlc_mutex_unlock( &priv->config_lock );
+#if defined(__APPLE__) || defined(__ANDROID__)
+    fsync (fd); /* Flush from OS */
+#else
+    fdatasync (fd); /* Flush from OS */
+#endif
+#if defined (_WIN32) || defined (__OS2__)
+    /* Windows cannot (re)move open files nor overwrite existing ones */
+    fclose (file);
+    vlc_unlink (permanent);
+#endif
+    /* Atomically replace the file... */
+    if (vlc_rename (temporary, permanent))
+        vlc_unlink (temporary);
+    /* (...then synchronize the directory, err, TODO...) */
+    /* ...and finally close the file */
+    vlc_mutex_unlock (&lock);
+#if !defined (_WIN32) && !defined (__OS2__)
+    fclose (file);
+#endif
 
-    return done ? VLC_SUCCESS : SaveConfigFile( p_this, NULL, true );
-}
+    free (temporary);
+    free (permanent);
+    return 0;
 
-int __config_SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name )
-{
-    return SaveConfigFile( p_this, psz_module_name, false );
+error:
+    free (temporary);
+    free (permanent);
+    return -1;
 }
 
-int ConfigStringToKey( const char *psz_key )
+int config_AutoSaveConfigFile( vlc_object_t *p_this )
 {
-    int i_key = 0;
-    unsigned int i;
-    const char *psz_parser = strchr( psz_key, '-' );
-    while( psz_parser && psz_parser != psz_key )
-    {
-        for( i = 0; i < sizeof(vlc_modifiers) / sizeof(key_descriptor_t); i++ )
-        {
-            if( !strncasecmp( vlc_modifiers[i].psz_key_string, psz_key,
-                              strlen( vlc_modifiers[i].psz_key_string ) ) )
-            {
-                i_key |= vlc_modifiers[i].i_key_code;
-            }
-        }
-        psz_key = psz_parser + 1;
-        psz_parser = strchr( psz_key, '-' );
-    }
-    for( i = 0; i < sizeof(vlc_keys) / sizeof( key_descriptor_t ); i++ )
-    {
-        if( !strcasecmp( vlc_keys[i].psz_key_string, psz_key ) )
-        {
-            i_key |= vlc_keys[i].i_key_code;
-            break;
-        }
-    }
-    return i_key;
-}
+    int ret = 0;
 
-char *ConfigKeyToString( int i_key )
-{
-    char *psz_key = malloc( 100 );
-    char *p;
-    size_t index;
+    assert( p_this );
 
-    if ( !psz_key )
+    vlc_rwlock_rdlock (&config_lock);
+    if (config_dirty)
     {
-        return NULL;
+        /* Note: this will get the read lock recursively. Ok. */
+        ret = config_SaveConfigFile (p_this);
+        config_dirty = (ret != 0);
     }
-    *psz_key = '\0';
-    p = psz_key;
+    vlc_rwlock_unlock (&config_lock);
 
-    for( index = 0; index < (sizeof(vlc_modifiers) / sizeof(key_descriptor_t));
-         index++ )
-    {
-        if( i_key & vlc_modifiers[index].i_key_code )
-        {
-            p += sprintf( p, "%s-", vlc_modifiers[index].psz_key_string );
-        }
-    }
-    for( index = 0; index < (sizeof(vlc_keys) / sizeof( key_descriptor_t));
-         index++)
-    {
-        if( (int)( i_key & ~KEY_MODIFIER ) == vlc_keys[index].i_key_code )
-        {
-            p += sprintf( p, "%s", vlc_keys[index].psz_key_string );
-            break;
-        }
-    }
-    return psz_key;
+    return ret;
 }
-