]> 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 0d3f6b6afb06553ca3bea140be53db47127c36f3..1c0b876883d138e8e084b5f82ffe8a4ded305bcb 100644 (file)
@@ -1,24 +1,24 @@
 /*****************************************************************************
  * 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
 #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_keys.h>
+#include <vlc_modules.h>
+#include <vlc_plugin.h>
 
 #include "configuration.h"
 #include "modules/modules.h"
@@ -83,7 +88,7 @@ static FILE *config_OpenConfigFile( vlc_object_t *p_obj )
                  psz_filename );
 
     }
-#if !( defined(WIN32) || defined(__APPLE__) || defined(SYS_BEOS) )
+#if !( defined(_WIN32) || defined(__APPLE__) || defined(__OS2__) )
     else if( p_stream == NULL && errno == ENOENT )
     {
         /* This is the fallback for pre XDG Base Directory
@@ -163,7 +168,7 @@ static int64_t strtoi (const char *str)
  * 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 )
 {
     FILE *file;
 
@@ -171,9 +176,6 @@ int config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name )
     if (file == NULL)
         return VLC_EGENERIC;
 
-    /* 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];
@@ -185,132 +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);
 
     vlc_rwlock_wrlock (&config_lock);
-    while (fgets (line, 1024, file) != NULL)
+    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:
-                {
-                    int64_t l = strtoi (psz_option_value);
-                    if ((l > p_item->max.i) || (l < p_item->min.i))
-                        errno = ERANGE;
-                    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 = 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:
-                    /* 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);
-                    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))
     {
@@ -319,7 +258,6 @@ 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);
@@ -403,36 +341,13 @@ 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)
 {
-    module_t *p_parser;
-    FILE *file = NULL;
-    char *permanent = NULL, *temporary = NULL;
-    char p_line[1024], *p_index2;
-    unsigned long i_sizebuf = 0;
-    char *p_bigbuffer = NULL, *p_index;
-    bool b_backup;
-    int i_index;
 
     if( config_PrepareDir( p_this ) )
     {
@@ -440,108 +355,36 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
         return -1;
     }
 
-    file = config_OpenConfigFile( p_this );
-    if( file != NULL )
+    /*
+     * 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)
+    {
+        free (permanent);
+        return -1;
+    }
+    else
     {
         struct stat st;
 
         /* 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 (fstat (fileno (file), &st)
-         || !(st.st_mode & S_IWUSR))
+        if (stat (permanent, &st) == 0 && !(st.st_mode & S_IWUSR))
         {
             msg_Err (p_this, "configuration file is read-only");
             goto error;
         }
-        i_sizebuf = ( st.st_size < LONG_MAX ) ? st.st_size : 0;
-    }
-
-    p_bigbuffer = p_index = malloc( i_sizebuf+1 );
-    if( !p_bigbuffer )
-        goto error;
-    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 ) )
-    {
-        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;
-            }
-        }
-
-        /* 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') )
-        {
-            strcpy( p_index, p_line );
-            p_index += strlen( p_line );
-        }
-    }
-    if( file )
-        fclose( file );
-    file = NULL;
-
-    /*
-     * Save module config in file
-     */
-    permanent = config_GetConfigFile (p_this);
-    if (!permanent)
-    {
-        module_list_free (list);
-        goto error;
-    }
-
-    if (asprintf (&temporary, "%s.%u", permanent, getpid ()) == -1)
-    {
-        temporary = NULL;
-        module_list_free (list);
-        goto error;
     }
 
     /* Configuration lock must be taken before vlcrc serializer below. */
     vlc_rwlock_rdlock (&config_lock);
 
-    /* The temporary configuration file is per-PID. Therefore SaveConfigFile()
+    /* 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);
@@ -551,17 +394,15 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
     {
         vlc_rwlock_unlock (&config_lock);
         vlc_mutex_unlock (&lock);
-        module_list_free (list);
         goto error;
     }
-    file = fdopen (fd, "wt");
+    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);
-        module_list_free (list);
         goto error;
     }
 
@@ -584,22 +425,17 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
     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
@@ -609,81 +445,41 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
              p_item < p_end;
              p_item++ )
         {
-            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;
 
-            /* 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 (IsConfigIntegerType (p_item->i_type))
             {
-                int64_t 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, "%"PRId64, 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);
@@ -695,12 +491,6 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
         freelocale (loc);
     }
 
-    /*
-     * Restore old settings from the config in file
-     */
-    fputs( p_bigbuffer, file );
-    free( p_bigbuffer );
-
     /*
      * Flush to disk and replace atomically
      */
@@ -710,14 +500,18 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
         vlc_unlink (temporary);
         vlc_mutex_unlock (&lock);
         msg_Err (p_this, "cannot write configuration file");
-        clearerr (file);
+        fclose (file);
         goto error;
     }
-#ifndef WIN32
-#ifdef __APPLE__
+#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))
@@ -725,14 +519,8 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
     /* (...then synchronize the directory, err, TODO...) */
     /* ...and finally close the file */
     vlc_mutex_unlock (&lock);
-#endif
+#if !defined (_WIN32) && !defined (__OS2__)
     fclose (file);
-#ifdef WIN32
-    /* Windows cannot remove open files nor overwrite existing ones */
-    vlc_unlink (permanent);
-    if (vlc_rename (temporary, permanent))
-        vlc_unlink (temporary);
-    vlc_mutex_unlock (&lock);
 #endif
 
     free (temporary);
@@ -740,50 +528,25 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
     return 0;
 
 error:
-    if( file )
-        fclose( file );
     free (temporary);
     free (permanent);
-    free( p_bigbuffer );
     return -1;
 }
 
 int config_AutoSaveConfigFile( vlc_object_t *p_this )
 {
-    int ret = VLC_SUCCESS;
-    bool save = false;
+    int ret = 0;
 
     assert( p_this );
 
-    /* Check if there's anything to save */
-    module_t **list = module_list_get (NULL);
     vlc_rwlock_rdlock (&config_lock);
-    for (size_t i_index = 0; list[i_index] && !save; i_index++)
+    if (config_dirty)
     {
-        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 && !save;
-             p_item++ )
-        {
-            save = p_item->b_autosave && p_item->b_dirty;
-        }
-    }
-
-    if (save)
         /* Note: this will get the read lock recursively. Ok. */
-        ret = SaveConfigFile (p_this, NULL, true);
+        ret = config_SaveConfigFile (p_this);
+        config_dirty = (ret != 0);
+    }
     vlc_rwlock_unlock (&config_lock);
 
-    module_list_free (list);
     return ret;
 }
-
-#undef config_SaveConfigFile
-int config_SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name )
-{
-    return SaveConfigFile( p_this, psz_module_name, false );
-}