/*****************************************************************************
* 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"
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
* 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;
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];
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 (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))
{
}
fclose (file);
- module_list_free (list);
if (loc != (locale_t)0)
{
uselocale (baseloc);
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 ) )
{
msg_Err( p_this, "no configuration directory" );
- goto error;
+ 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);
{
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;
}
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++ )
+ module_t **list = module_list_get (NULL);
+ module_t *p_parser;
+ for (int i = 0; (p_parser = list[i]) != NULL; 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
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);
freelocale (loc);
}
- /*
- * Restore old settings from the config in file
- */
- fputs( p_bigbuffer, file );
- free( p_bigbuffer );
-
/*
* Flush to disk and replace atomically
*/
fflush (file); /* Flush from run-time */
-#ifndef WIN32
-#ifdef __APPLE__
+ if (ferror (file))
+ {
+ vlc_unlink (temporary);
+ vlc_mutex_unlock (&lock);
+ msg_Err (p_this, "cannot write configuration file");
+ fclose (file);
+ goto error;
+ }
+#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))
/* (...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);
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 );
-}