contrib: schroedinger: remove obsolete patches
[vlc/gmpfix.git] / src / config / file.c
blob7bab109d00bf72c5ec02bf7c3eefa55a70315ed6
1 /*****************************************************************************
2 * file.c: configuration file handling
3 *****************************************************************************
4 * Copyright (C) 2001-2007 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <errno.h> /* errno */
29 #include <assert.h>
30 #include <limits.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #ifdef __APPLE__
34 # include <xlocale.h>
35 #elif defined(HAVE_USELOCALE)
36 #include <locale.h>
37 #endif
38 #include <unistd.h>
40 #include <vlc_common.h>
41 #include "../libvlc.h"
42 #include <vlc_charset.h>
43 #include <vlc_fs.h>
44 #include <vlc_keys.h>
45 #include <vlc_modules.h>
46 #include <vlc_plugin.h>
48 #include "configuration.h"
49 #include "modules/modules.h"
51 static inline char *strdupnull (const char *src)
53 return src ? strdup (src) : NULL;
56 /**
57 * Get the user's configuration file
59 static char *config_GetConfigFile( vlc_object_t *obj )
61 char *psz_file = var_CreateGetNonEmptyString( obj, "config" );
62 var_Destroy( obj, "config" );
63 if( psz_file == NULL )
65 char *psz_dir = config_GetUserDir( VLC_CONFIG_DIR );
67 if( asprintf( &psz_file, "%s" DIR_SEP CONFIG_FILE, psz_dir ) == -1 )
68 psz_file = NULL;
69 free( psz_dir );
71 return psz_file;
74 static FILE *config_OpenConfigFile( vlc_object_t *p_obj )
76 char *psz_filename = config_GetConfigFile( p_obj );
77 if( psz_filename == NULL )
78 return NULL;
80 msg_Dbg( p_obj, "opening config file (%s)", psz_filename );
82 FILE *p_stream = vlc_fopen( psz_filename, "rt" );
83 if( p_stream == NULL && errno != ENOENT )
85 msg_Err( p_obj, "cannot open config file (%s): %s",
86 psz_filename, vlc_strerror_c(errno) );
89 #if !( defined(_WIN32) || defined(__APPLE__) || defined(__OS2__) )
90 else if( p_stream == NULL && errno == ENOENT )
92 /* This is the fallback for pre XDG Base Directory
93 * Specification configs */
94 char *home = config_GetUserDir(VLC_HOME_DIR);
95 char *psz_old;
97 if( home != NULL
98 && asprintf( &psz_old, "%s/.vlc/" CONFIG_FILE,
99 home ) != -1 )
101 p_stream = vlc_fopen( psz_old, "rt" );
102 if( p_stream )
104 /* Old config file found. We want to write it at the
105 * new location now. */
106 msg_Info( p_obj->p_libvlc, "Found old config file at %s. "
107 "VLC will now use %s.", psz_old, psz_filename );
108 char *psz_readme;
109 if( asprintf(&psz_readme,"%s/.vlc/README",
110 home ) != -1 )
112 FILE *p_readme = vlc_fopen( psz_readme, "wt" );
113 if( p_readme )
115 fprintf( p_readme, "The VLC media player "
116 "configuration folder has moved to comply\n"
117 "with the XDG Base Directory Specification "
118 "version 0.6. Your\nconfiguration has been "
119 "copied to the new location:\n%s\nYou can "
120 "delete this directory and all its contents.",
121 psz_filename);
122 fclose( p_readme );
124 free( psz_readme );
126 /* Remove the old configuration file so that --reset-config
127 * can work properly. Fortunately, Linux allows removing
128 * open files - with most filesystems. */
129 unlink( psz_old );
131 free( psz_old );
133 free( home );
135 #endif
136 free( psz_filename );
137 return p_stream;
141 static int64_t strtoi (const char *str)
143 char *end;
144 long long l;
146 errno = 0;
147 l = strtoll (str, &end, 0);
149 if (!errno)
151 #if (LLONG_MAX > 0x7fffffffffffffffLL)
152 if (l > 0x7fffffffffffffffLL
153 || l < -0x8000000000000000LL)
154 errno = ERANGE;
155 #endif
156 if (*end)
157 errno = EINVAL;
159 return l;
162 #undef config_LoadConfigFile
163 /*****************************************************************************
164 * config_LoadConfigFile: loads the configuration file.
165 *****************************************************************************
166 * This function is called to load the config options stored in the config
167 * file.
168 *****************************************************************************/
169 int config_LoadConfigFile( vlc_object_t *p_this )
171 FILE *file;
173 file = config_OpenConfigFile (p_this);
174 if (file == NULL)
175 return VLC_EGENERIC;
177 /* Look for UTF-8 Byte Order Mark */
178 char * (*convert) (const char *) = strdupnull;
179 char bom[3];
181 if ((fread (bom, 1, 3, file) != 3)
182 || memcmp (bom, "\xEF\xBB\xBF", 3))
184 convert = FromLocaleDup;
185 rewind (file); /* no BOM, rewind */
188 char *line = NULL;
189 size_t bufsize;
190 ssize_t linelen;
192 /* Ensure consistent number formatting... */
193 locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
194 locale_t baseloc = uselocale (loc);
196 vlc_rwlock_wrlock (&config_lock);
197 while ((linelen = getline (&line, &bufsize, file)) != -1)
199 line[linelen - 1] = '\0'; /* trim newline */
201 /* Ignore comments, section and empty lines */
202 if (memchr ("#[", line[0], 3) != NULL)
203 continue;
205 /* look for option name */
206 const char *psz_option_name = line;
208 char *ptr = strchr (line, '=');
209 if (ptr == NULL)
210 continue; /* syntax error */
211 *ptr = '\0';
213 module_config_t *item = config_FindConfig (p_this, psz_option_name);
214 if (item == NULL)
215 continue;
217 const char *psz_option_value = ptr + 1;
218 switch (CONFIG_CLASS(item->i_type))
220 case CONFIG_ITEM_BOOL:
221 case CONFIG_ITEM_INTEGER:
223 int64_t l;
225 errno = 0;
226 l = strtoi (psz_option_value);
227 if ((l > item->max.i) || (l < item->min.i))
228 errno = ERANGE;
229 if (errno)
230 msg_Warn (p_this, "Integer value (%s) for %s: %s",
231 psz_option_value, psz_option_name,
232 vlc_strerror_c(errno));
233 else
234 item->value.i = l;
235 break;
238 case CONFIG_ITEM_FLOAT:
239 if (!*psz_option_value)
240 break; /* ignore empty option */
241 item->value.f = (float)atof (psz_option_value);
242 break;
244 default:
245 free ((char *)item->value.psz);
246 item->value.psz = convert (psz_option_value);
247 break;
250 vlc_rwlock_unlock (&config_lock);
251 free (line);
253 if (ferror (file))
255 msg_Err (p_this, "error reading configuration: %s",
256 vlc_strerror_c(errno));
257 clearerr (file);
259 fclose (file);
261 if (loc != (locale_t)0)
263 uselocale (baseloc);
264 freelocale (loc);
266 return 0;
269 /*****************************************************************************
270 * config_CreateDir: Create configuration directory if it doesn't exist.
271 *****************************************************************************/
272 int config_CreateDir( vlc_object_t *p_this, const char *psz_dirname )
274 if( !psz_dirname || !*psz_dirname ) return -1;
276 if( vlc_mkdir( psz_dirname, 0700 ) == 0 )
277 return 0;
279 switch( errno )
281 case EEXIST:
282 return 0;
284 case ENOENT:
286 /* Let's try to create the parent directory */
287 char psz_parent[strlen( psz_dirname ) + 1], *psz_end;
288 strcpy( psz_parent, psz_dirname );
290 psz_end = strrchr( psz_parent, DIR_SEP_CHAR );
291 if( psz_end && psz_end != psz_parent )
293 *psz_end = '\0';
294 if( config_CreateDir( p_this, psz_parent ) == 0 )
296 if( !vlc_mkdir( psz_dirname, 0700 ) )
297 return 0;
303 msg_Warn( p_this, "could not create %s: %s", psz_dirname,
304 vlc_strerror_c(errno) );
305 return -1;
308 static int
309 config_Write (FILE *file, const char *desc, const char *type,
310 bool comment, const char *name, const char *fmt, ...)
312 va_list ap;
313 int ret;
315 if (desc == NULL)
316 desc = "?";
318 if (fprintf (file, "# %s (%s)\n%s%s=", desc, vlc_gettext (type),
319 comment ? "#" : "", name) < 0)
320 return -1;
322 va_start (ap, fmt);
323 ret = vfprintf (file, fmt, ap);
324 va_end (ap);
325 if (ret < 0)
326 return -1;
328 if (fputs ("\n\n", file) == EOF)
329 return -1;
330 return 0;
334 static int config_PrepareDir (vlc_object_t *obj)
336 char *psz_configdir = config_GetUserDir (VLC_CONFIG_DIR);
337 if (psz_configdir == NULL)
338 return -1;
340 int ret = config_CreateDir (obj, psz_configdir);
341 free (psz_configdir);
342 return ret;
345 #undef config_SaveConfigFile
347 * Saves the in-memory configuration into a file.
348 * @return 0 on success, -1 on error.
350 int config_SaveConfigFile (vlc_object_t *p_this)
353 if( config_PrepareDir( p_this ) )
355 msg_Err( p_this, "no configuration directory" );
356 return -1;
360 * Save module config in file
362 char *temporary;
363 char *permanent = config_GetConfigFile (p_this);
364 if (permanent == NULL)
365 return -1;
366 if (asprintf (&temporary, "%s.%u", permanent, getpid ()) == -1)
368 free (permanent);
369 return -1;
371 else
373 struct stat st;
375 /* Some users make vlcrc read-only to prevent changes.
376 * The atomic replacement scheme breaks this "feature",
377 * so we check for read-only by hand. */
378 if (stat (permanent, &st) == 0 && !(st.st_mode & S_IWUSR))
380 msg_Err (p_this, "configuration file is read-only");
381 goto error;
385 /* Configuration lock must be taken before vlcrc serializer below. */
386 vlc_rwlock_rdlock (&config_lock);
388 /* The temporary configuration file is per-PID. Therefore this function
389 * should be serialized against itself within a given process. */
390 static vlc_mutex_t lock = VLC_STATIC_MUTEX;
391 vlc_mutex_lock (&lock);
393 int fd = vlc_open (temporary, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
394 if (fd == -1)
396 vlc_rwlock_unlock (&config_lock);
397 vlc_mutex_unlock (&lock);
398 goto error;
400 FILE *file = fdopen (fd, "wt");
401 if (file == NULL)
403 msg_Err (p_this, "cannot create configuration file: %s",
404 vlc_strerror_c(errno));
405 vlc_rwlock_unlock (&config_lock);
406 close (fd);
407 vlc_mutex_unlock (&lock);
408 goto error;
411 fprintf( file,
412 "\xEF\xBB\xBF###\n"
413 "### "PACKAGE_NAME" "PACKAGE_VERSION"\n"
414 "###\n"
415 "\n"
416 "###\n"
417 "### lines beginning with a '#' character are comments\n"
418 "###\n"
419 "\n" );
421 /* Ensure consistent number formatting... */
422 locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
423 locale_t baseloc = uselocale (loc);
425 /* We would take the config lock here. But this would cause a lock
426 * inversion with the serializer above and config_AutoSaveConfigFile().
427 vlc_rwlock_rdlock (&config_lock);*/
429 /* Look for the selected module, if NULL then save everything */
430 size_t count;
431 module_t **list = module_list_get (&count);
432 for (size_t i = 0; i < count; i++)
434 module_t *p_parser = list[i];
435 module_config_t *p_item, *p_end;
437 if( !p_parser->i_config_items )
438 continue;
440 fprintf( file, "[%s]", module_get_object (p_parser) );
441 if( p_parser->psz_longname )
442 fprintf( file, " # %s\n\n", p_parser->psz_longname );
443 else
444 fprintf( file, "\n\n" );
446 for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize;
447 p_item < p_end;
448 p_item++ )
450 if (!CONFIG_ITEM(p_item->i_type) /* ignore hint */
451 || p_item->b_removed /* ignore deprecated option */
452 || p_item->b_unsaveable) /* ignore volatile option */
453 continue;
455 if (IsConfigIntegerType (p_item->i_type))
457 int64_t val = p_item->value.i;
458 config_Write (file, p_item->psz_text,
459 (CONFIG_CLASS(p_item->i_type) == CONFIG_ITEM_BOOL)
460 ? N_("boolean") : N_("integer"),
461 val == p_item->orig.i,
462 p_item->psz_name, "%"PRId64, val);
464 else
465 if (IsConfigFloatType (p_item->i_type))
467 float val = p_item->value.f;
468 config_Write (file, p_item->psz_text, N_("float"),
469 val == p_item->orig.f,
470 p_item->psz_name, "%f", val);
472 else
474 const char *psz_value = p_item->value.psz;
475 bool modified;
477 assert (IsConfigStringType (p_item->i_type));
479 modified = !!strcmp (psz_value ? psz_value : "",
480 p_item->orig.psz ? p_item->orig.psz : "");
481 config_Write (file, p_item->psz_text, N_("string"),
482 !modified, p_item->psz_name, "%s",
483 psz_value ? psz_value : "");
487 vlc_rwlock_unlock (&config_lock);
489 module_list_free (list);
490 if (loc != (locale_t)0)
492 uselocale (baseloc);
493 freelocale (loc);
497 * Flush to disk and replace atomically
499 fflush (file); /* Flush from run-time */
500 if (ferror (file))
502 vlc_unlink (temporary);
503 vlc_mutex_unlock (&lock);
504 msg_Err (p_this, "cannot write configuration file");
505 fclose (file);
506 goto error;
508 #if defined(__APPLE__) || defined(__ANDROID__)
509 fsync (fd); /* Flush from OS */
510 #else
511 fdatasync (fd); /* Flush from OS */
512 #endif
513 #if defined (_WIN32) || defined (__OS2__)
514 /* Windows cannot (re)move open files nor overwrite existing ones */
515 fclose (file);
516 vlc_unlink (permanent);
517 #endif
518 /* Atomically replace the file... */
519 if (vlc_rename (temporary, permanent))
520 vlc_unlink (temporary);
521 /* (...then synchronize the directory, err, TODO...) */
522 /* ...and finally close the file */
523 vlc_mutex_unlock (&lock);
524 #if !defined (_WIN32) && !defined (__OS2__)
525 fclose (file);
526 #endif
528 free (temporary);
529 free (permanent);
530 return 0;
532 error:
533 free (temporary);
534 free (permanent);
535 return -1;
538 int config_AutoSaveConfigFile( vlc_object_t *p_this )
540 int ret = 0;
542 assert( p_this );
544 vlc_rwlock_rdlock (&config_lock);
545 if (config_dirty)
547 /* Note: this will get the read lock recursively. Ok. */
548 ret = config_SaveConfigFile (p_this);
549 config_dirty = (ret != 0);
551 vlc_rwlock_unlock (&config_lock);
553 return ret;