]> git.sesse.net Git - vlc/blob - src/config/file.c
core: config_GetPsz -> var_CreateGetNonEmptyString
[vlc] / src / config / file.c
1 /*****************************************************************************
2  * file.c: configuration file handling
3  *****************************************************************************
4  * Copyright (C) 2001-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
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
39 #include <vlc_common.h>
40 #include "../libvlc.h"
41 #include "vlc_charset.h"
42 #include "vlc_keys.h"
43
44 #include "configuration.h"
45 #include "modules/modules.h"
46
47 static inline char *strdupnull (const char *src)
48 {
49     return src ? strdup (src) : NULL;
50 }
51
52 /**
53  * Get the user's configuration file
54  */
55 static char *config_GetConfigFile( vlc_object_t *obj )
56 {
57     char *psz_file = var_CreateGetNonEmptyString( obj, "config" );
58     var_Destroy( obj, "config" );
59     if( psz_file == NULL )
60     {
61         char *psz_dir = config_GetUserDir( VLC_CONFIG_DIR );
62
63         if( asprintf( &psz_file, "%s" DIR_SEP CONFIG_FILE, psz_dir ) == -1 )
64             psz_file = NULL;
65         free( psz_dir );
66     }
67     return psz_file;
68 }
69
70 static FILE *config_OpenConfigFile( vlc_object_t *p_obj )
71 {
72     char *psz_filename = config_GetConfigFile( p_obj );
73     if( psz_filename == NULL )
74         return NULL;
75
76     msg_Dbg( p_obj, "opening config file (%s)", psz_filename );
77
78     FILE *p_stream = utf8_fopen( psz_filename, "rt" );
79     if( p_stream == NULL && errno != ENOENT )
80     {
81         msg_Err( p_obj, "cannot open config file (%s): %m",
82                  psz_filename );
83
84     }
85 #if !( defined(WIN32) || defined(__APPLE__) || defined(SYS_BEOS) )
86     else if( p_stream == NULL && errno == ENOENT )
87     {
88         /* This is the fallback for pre XDG Base Directory
89          * Specification configs */
90         char *home = config_GetUserDir(VLC_HOME_DIR);
91         char *psz_old;
92
93         if( home != NULL
94          && asprintf( &psz_old, "%s/.vlc/" CONFIG_FILE,
95                       home ) != -1 )
96         {
97             p_stream = utf8_fopen( psz_old, "rt" );
98             if( p_stream )
99             {
100                 /* Old config file found. We want to write it at the
101                  * new location now. */
102                 msg_Info( p_obj->p_libvlc, "Found old config file at %s. "
103                           "VLC will now use %s.", psz_old, psz_filename );
104                 char *psz_readme;
105                 if( asprintf(&psz_readme,"%s/.vlc/README",
106                              home ) != -1 )
107                 {
108                     FILE *p_readme = utf8_fopen( psz_readme, "wt" );
109                     if( p_readme )
110                     {
111                         fprintf( p_readme, "The VLC media player "
112                                  "configuration folder has moved to comply\n"
113                                  "with the XDG Base Directory Specification "
114                                  "version 0.6. Your\nconfiguration has been "
115                                  "copied to the new location:\n%s\nYou can "
116                                  "delete this directory and all its contents.",
117                                   psz_filename);
118                         fclose( p_readme );
119                     }
120                     free( psz_readme );
121                 }
122                 /* Remove the old configuration file so that --reset-config
123                  * can work properly. Fortunately, Linux allows removing
124                  * open files - with most filesystems. */
125                 unlink( psz_old );
126             }
127             free( psz_old );
128         }
129         free( home );
130     }
131 #endif
132     free( psz_filename );
133     return p_stream;
134 }
135
136
137 static int strtoi (const char *str)
138 {
139     char *end;
140     long l;
141
142     errno = 0;
143     l = strtol (str, &end, 0);
144
145     if (!errno)
146     {
147         if ((l > INT_MAX) || (l < INT_MIN))
148             errno = ERANGE;
149         if (*end)
150             errno = EINVAL;
151     }
152     return (int)l;
153 }
154
155
156 /*****************************************************************************
157  * config_LoadConfigFile: loads the configuration file.
158  *****************************************************************************
159  * This function is called to load the config options stored in the config
160  * file.
161  *****************************************************************************/
162 int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name )
163 {
164     FILE *file;
165
166     file = config_OpenConfigFile (p_this);
167     if (file == NULL)
168         return VLC_EGENERIC;
169
170     /* Look for the selected module, if NULL then save everything */
171     module_t **list = module_list_get (NULL);
172
173     /* Look for UTF-8 Byte Order Mark */
174     char * (*convert) (const char *) = strdupnull;
175     char bom[3];
176
177     if ((fread (bom, 1, 3, file) != 3)
178      || memcmp (bom, "\xEF\xBB\xBF", 3))
179     {
180         convert = FromLocaleDup;
181         rewind (file); /* no BOM, rewind */
182     }
183
184     module_t *module = NULL;
185     char line[1024], section[1022];
186     section[0] = '\0';
187
188     /* Ensure consistent number formatting... */
189     locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
190     locale_t baseloc = uselocale (loc);
191
192     while (fgets (line, 1024, file) != NULL)
193     {
194         /* Ignore comments and empty lines */
195         switch (line[0])
196         {
197             case '#':
198             case '\n':
199             case '\0':
200                 continue;
201         }
202
203         if (line[0] == '[')
204         {
205             char *ptr = strchr (line, ']');
206             if (ptr == NULL)
207                 continue; /* syntax error; */
208             *ptr = '\0';
209
210             /* New section ( = a given module) */
211             strcpy (section, line + 1);
212             module = NULL;
213
214             if ((psz_module_name == NULL)
215              || (strcmp (psz_module_name, section) == 0))
216             {
217                 for (int i = 0; list[i]; i++)
218                 {
219                     module_t *m = list[i];
220
221                     if ((strcmp (section, m->psz_object_name) == 0)
222                      && (m->i_config_items > 0)) /* ignore config-less modules */
223                     {
224                         module = m;
225                         if (psz_module_name != NULL)
226                             msg_Dbg (p_this,
227                                      "loading config for module \"%s\"",
228                                      section);
229                         break;
230                     }
231                 }
232             }
233
234             continue;
235         }
236
237         if (module == NULL)
238             continue; /* no need to parse if there is no matching module */
239
240         char *ptr = strchr (line, '\n');
241         if (ptr != NULL)
242             *ptr = '\0';
243
244         /* look for option name */
245         const char *psz_option_name = line;
246
247         ptr = strchr (line, '=');
248         if (ptr == NULL)
249             continue; /* syntax error */
250
251         *ptr = '\0';
252         const char *psz_option_value = ptr + 1;
253
254         /* try to match this option with one of the module's options */
255         for (size_t i = 0; i < module->confsize; i++)
256         {
257             module_config_t *p_item = module->p_config + i;
258
259             if ((p_item->i_type & CONFIG_HINT)
260              || strcmp (p_item->psz_name, psz_option_name))
261                 continue;
262
263             /* We found it */
264             errno = 0;
265
266             vlc_mutex_lock( p_item->p_lock );
267             switch( p_item->i_type )
268             {
269                 case CONFIG_ITEM_BOOL:
270                 case CONFIG_ITEM_INTEGER:
271                 {
272                     long l = strtoi (psz_option_value);
273                     if (errno)
274                         msg_Warn (p_this, "Integer value (%s) for %s: %m",
275                                   psz_option_value, psz_option_name);
276                     else
277                         p_item->saved.i = p_item->value.i = (int)l;
278                     break;
279                 }
280
281                 case CONFIG_ITEM_FLOAT:
282                     if( !*psz_option_value )
283                         break;                    /* ignore empty option */
284                     p_item->value.f = (float)atof (psz_option_value);
285                     p_item->saved.f = p_item->value.f;
286                     break;
287
288                 case CONFIG_ITEM_KEY:
289                     if( !*psz_option_value )
290                         break;                    /* ignore empty option */
291                     p_item->value.i = ConfigStringToKey(psz_option_value);
292                     p_item->saved.i = p_item->value.i;
293                     break;
294
295                 default:
296                     /* free old string */
297                     free( (char*) p_item->value.psz );
298                     free( (char*) p_item->saved.psz );
299
300                     p_item->value.psz = convert (psz_option_value);
301                     p_item->saved.psz = strdupnull (p_item->value.psz);
302                     break;
303             }
304             vlc_mutex_unlock( p_item->p_lock );
305             break;
306         }
307     }
308
309     if (ferror (file))
310     {
311         msg_Err (p_this, "error reading configuration: %m");
312         clearerr (file);
313     }
314     fclose (file);
315
316     module_list_free (list);
317     if (loc != (locale_t)0)
318     {
319         uselocale (baseloc);
320         freelocale (loc);
321     }
322     return 0;
323 }
324
325 /*****************************************************************************
326  * config_CreateDir: Create configuration directory if it doesn't exist.
327  *****************************************************************************/
328 int config_CreateDir( vlc_object_t *p_this, const char *psz_dirname )
329 {
330     if( !psz_dirname || !*psz_dirname ) return -1;
331
332     if( utf8_mkdir( psz_dirname, 0700 ) == 0 )
333         return 0;
334
335     switch( errno )
336     {
337         case EEXIST:
338             return 0;
339
340         case ENOENT:
341         {
342             /* Let's try to create the parent directory */
343             char psz_parent[strlen( psz_dirname ) + 1], *psz_end;
344             strcpy( psz_parent, psz_dirname );
345
346             psz_end = strrchr( psz_parent, DIR_SEP_CHAR );
347             if( psz_end && psz_end != psz_parent )
348             {
349                 *psz_end = '\0';
350                 if( config_CreateDir( p_this, psz_parent ) == 0 )
351                 {
352                     if( !utf8_mkdir( psz_dirname, 0700 ) )
353                         return 0;
354                 }
355             }
356         }
357     }
358
359     msg_Err( p_this, "could not create %s: %m", psz_dirname );
360     return -1;
361 }
362
363 static int
364 config_Write (FILE *file, const char *desc, const char *type,
365               bool comment, const char *name, const char *fmt, ...)
366 {
367     va_list ap;
368     int ret;
369
370     if (desc == NULL)
371         desc = "?";
372
373     if (fprintf (file, "# %s (%s)\n%s%s=", desc, vlc_gettext (type),
374                  comment ? "#" : "", name) < 0)
375         return -1;
376
377     va_start (ap, fmt);
378     ret = vfprintf (file, fmt, ap);
379     va_end (ap);
380     if (ret < 0)
381         return -1;
382
383     if (fputs ("\n\n", file) == EOF)
384         return -1;
385     return 0;
386 }
387
388
389 static int config_PrepareDir (vlc_object_t *obj)
390 {
391     char *psz_configdir = config_GetUserDir (VLC_CONFIG_DIR);
392     if (psz_configdir == NULL)
393         return -1;
394
395     int ret = config_CreateDir (obj, psz_configdir);
396     free (psz_configdir);
397     return ret;
398 }
399
400 /*****************************************************************************
401  * config_SaveConfigFile: Save a module's config options.
402  *****************************************************************************
403  * This will save the specified module's config options to the config file.
404  * If psz_module_name is NULL then we save all the modules config options.
405  * It's no use to save the config options that kept their default values, so
406  * we'll try to be a bit clever here.
407  *
408  * When we save we mustn't delete the config options of the modules that
409  * haven't been loaded. So we cannot just create a new config file with the
410  * config structures we've got in memory.
411  * I don't really know how to deal with this nicely, so I will use a completly
412  * dumb method ;-)
413  * I will load the config file in memory, but skipping all the sections of the
414  * modules we want to save. Then I will create a brand new file, dump the file
415  * loaded in memory and then append the sections of the modules we want to
416  * save.
417  * Really stupid no ?
418  *****************************************************************************/
419 static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name,
420                            bool b_autosave )
421 {
422     module_t *p_parser;
423     FILE *file = NULL;
424     char *permanent = NULL, *temporary = NULL;
425     char p_line[1024], *p_index2;
426     unsigned long i_sizebuf = 0;
427     char *p_bigbuffer = NULL, *p_index;
428     bool b_backup;
429     int i_index;
430
431     if( config_PrepareDir( p_this ) )
432     {
433         msg_Err( p_this, "no configuration directory" );
434         goto error;
435     }
436
437     file = config_OpenConfigFile( p_this );
438     if( file != NULL )
439     {
440         struct stat st;
441
442         /* Some users make vlcrc read-only to prevent changes.
443          * The atomic replacement scheme breaks this "feature",
444          * so we check for read-only by hand. */
445         if (fstat (fileno (file), &st)
446          || !(st.st_mode & S_IWUSR))
447         {
448             msg_Err (p_this, "configuration file is read-only");
449             goto error;
450         }
451         i_sizebuf = ( st.st_size < LONG_MAX ) ? st.st_size : 0;
452     }
453
454     p_bigbuffer = p_index = malloc( i_sizebuf+1 );
455     if( !p_bigbuffer )
456         goto error;
457     p_bigbuffer[0] = 0;
458
459     /* List all available modules */
460     module_t **list = module_list_get (NULL);
461
462     /* backup file into memory, we only need to backup the sections we won't
463      * save later on */
464     b_backup = false;
465     while( file && fgets( p_line, 1024, file ) )
466     {
467         if( (p_line[0] == '[') && (p_index2 = strchr(p_line,']')))
468         {
469
470             /* we found a section, check if we need to do a backup */
471             for( i_index = 0; (p_parser = list[i_index]) != NULL; i_index++ )
472             {
473                 if( ((p_index2 - &p_line[1])
474                        == (int)strlen(p_parser->psz_object_name) )
475                     && !memcmp( &p_line[1], p_parser->psz_object_name,
476                                 strlen(p_parser->psz_object_name) ) )
477                 {
478                     if( !psz_module_name )
479                         break;
480                     else if( !strcmp( psz_module_name,
481                                       p_parser->psz_object_name ) )
482                         break;
483                 }
484             }
485
486             if( list[i_index] == NULL )
487             {
488                 /* we don't have this section in our list so we need to back
489                  * it up */
490                 *p_index2 = 0;
491 #if 0
492                 msg_Dbg( p_this, "backing up config for unknown module \"%s\"",
493                                  &p_line[1] );
494 #endif
495                 *p_index2 = ']';
496
497                 b_backup = true;
498             }
499             else
500             {
501                 b_backup = false;
502             }
503         }
504
505         /* save line if requested and line is valid (doesn't begin with a
506          * space, tab, or eol) */
507         if( b_backup && (p_line[0] != '\n') && (p_line[0] != ' ')
508             && (p_line[0] != '\t') )
509         {
510             strcpy( p_index, p_line );
511             p_index += strlen( p_line );
512         }
513     }
514     if( file )
515         fclose( file );
516     file = NULL;
517
518     /*
519      * Save module config in file
520      */
521     permanent = config_GetConfigFile (p_this);
522     if (!permanent)
523     {
524         module_list_free (list);
525         goto error;
526     }
527
528     if (asprintf (&temporary, "%s.%u", permanent, getpid ()) == -1)
529     {
530         temporary = NULL;
531         module_list_free (list);
532         goto error;
533     }
534
535     /* The temporary configuration file is per-PID. Therefore SaveConfigFile()
536      * should be serialized against itself within a given process. */
537     static vlc_mutex_t lock = VLC_STATIC_MUTEX;
538     vlc_mutex_lock (&lock);
539
540     int fd = utf8_open (temporary, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
541     if (fd == -1)
542     {
543         vlc_mutex_unlock (&lock);
544         module_list_free (list);
545         goto error;
546     }
547     file = fdopen (fd, "wt");
548     if (file == NULL)
549     {
550         close (fd);
551         vlc_mutex_unlock (&lock);
552         module_list_free (list);
553         goto error;
554     }
555
556     fprintf( file, "\xEF\xBB\xBF###\n###  " COPYRIGHT_MESSAGE "\n###\n\n"
557        "###\n### lines beginning with a '#' character are comments\n###\n\n" );
558
559     /* Ensure consistent number formatting... */
560     locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
561     locale_t baseloc = uselocale (loc);
562
563     /* Look for the selected module, if NULL then save everything */
564     for( i_index = 0; (p_parser = list[i_index]) != NULL; i_index++ )
565     {
566         module_config_t *p_item, *p_end;
567
568         if( psz_module_name && strcmp( psz_module_name,
569                                        p_parser->psz_object_name ) )
570             continue;
571
572         if( !p_parser->i_config_items )
573             continue;
574
575         if( psz_module_name )
576             msg_Dbg( p_this, "saving config for module \"%s\"",
577                      p_parser->psz_object_name );
578
579         fprintf( file, "[%s]", p_parser->psz_object_name );
580         if( p_parser->psz_longname )
581             fprintf( file, " # %s\n\n", p_parser->psz_longname );
582         else
583             fprintf( file, "\n\n" );
584
585         for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize;
586              p_item < p_end;
587              p_item++ )
588         {
589             if ((p_item->i_type & CONFIG_HINT) /* ignore hint */
590              || p_item->b_removed              /* ignore deprecated option */
591              || p_item->b_unsaveable)          /* ignore volatile option */
592                 continue;
593
594             vlc_mutex_lock (p_item->p_lock);
595
596             /* Do not save the new value in the configuration file
597              * if doing an autosave, and the item is not an "autosaved" one. */
598             bool b_retain = b_autosave && !p_item->b_autosave;
599
600             if (IsConfigIntegerType (p_item->i_type))
601             {
602                 int val = b_retain ? p_item->saved.i : p_item->value.i;
603                 if (p_item->i_type == CONFIG_ITEM_KEY)
604                 {
605                     char *psz_key = ConfigKeyToString (val);
606                     config_Write (file, p_item->psz_text, N_("key"),
607                                   val == p_item->orig.i,
608                                   p_item->psz_name, "%s",
609                                   psz_key ? psz_key : "");
610                     free (psz_key);
611                 }
612                 else
613                     config_Write (file, p_item->psz_text,
614                                   (p_item->i_type == CONFIG_ITEM_BOOL)
615                                       ? N_("boolean") : N_("integer"),
616                                   val == p_item->orig.i,
617                                   p_item->psz_name, "%d", val);
618                 p_item->saved.i = val;
619             }
620             else
621             if (IsConfigFloatType (p_item->i_type))
622             {
623                 float val = b_retain ? p_item->saved.f : p_item->value.f;
624                 config_Write (file, p_item->psz_text, N_("float"),
625                               val == p_item->orig.f,
626                               p_item->psz_name, "%f", val);
627                 p_item->saved.f = val;
628             }
629             else
630             {
631                 const char *psz_value = b_retain ? p_item->saved.psz
632                                                  : p_item->value.psz;
633                 bool modified;
634
635                 assert (IsConfigStringType (p_item->i_type));
636
637                 if (b_retain && (psz_value == NULL)) /* FIXME: hack */
638                     psz_value = p_item->orig.psz;
639
640                 modified =
641                     (psz_value != NULL)
642                         ? ((p_item->orig.psz != NULL)
643                             ? (strcmp (psz_value, p_item->orig.psz) != 0)
644                             : true)
645                         : (p_item->orig.psz != NULL);
646
647                 config_Write (file, p_item->psz_text, N_("string"),
648                               !modified, p_item->psz_name, "%s",
649                               psz_value ? psz_value : "");
650
651                 if ( !b_retain )
652                 {
653
654                     free ((char *)p_item->saved.psz);
655                     if( (psz_value && p_item->orig.psz &&
656                          strcmp( psz_value, p_item->orig.psz )) ||
657                         !psz_value || !p_item->orig.psz)
658                         p_item->saved.psz = strdupnull (psz_value);
659                     else
660                         p_item->saved.psz = NULL;
661                 }
662             }
663
664             if (!b_retain)
665                 p_item->b_dirty = false;
666             vlc_mutex_unlock (p_item->p_lock);
667         }
668     }
669
670     module_list_free (list);
671     if (loc != (locale_t)0)
672     {
673         uselocale (baseloc);
674         freelocale (loc);
675     }
676
677     /*
678      * Restore old settings from the config in file
679      */
680     fputs( p_bigbuffer, file );
681     free( p_bigbuffer );
682
683     /*
684      * Flush to disk and replace atomically
685      */
686     fflush (file); /* Flush from run-time */
687 #ifndef WIN32
688     fdatasync (fd); /* Flush from OS */
689     /* Atomically replace the file... */
690     if (utf8_rename (temporary, permanent))
691         utf8_unlink (temporary);
692     /* (...then synchronize the directory, err, TODO...) */
693     /* ...and finally close the file */
694     vlc_mutex_unlock (&lock);
695 #endif
696     fclose (file);
697 #ifdef WIN32
698     /* Windows cannot remove open files nor overwrite existing ones */
699     utf8_unlink (permanent);
700     if (utf8_rename (temporary, permanent))
701         utf8_unlink (temporary);
702     vlc_mutex_unlock (&lock);
703 #endif
704
705     free (temporary);
706     free (permanent);
707     return 0;
708
709 error:
710     if( file )
711         fclose( file );
712     free (temporary);
713     free (permanent);
714     free( p_bigbuffer );
715     return -1;
716 }
717
718 int config_AutoSaveConfigFile( vlc_object_t *p_this )
719 {
720     size_t i_index;
721     bool save = false;
722
723     assert( p_this );
724
725     /* Check if there's anything to save */
726     module_t **list = module_list_get (NULL);
727     for( i_index = 0; list[i_index] && !save; i_index++ )
728     {
729         module_t *p_parser = list[i_index];
730         module_config_t *p_item, *p_end;
731
732         if( !p_parser->i_config_items ) continue;
733
734         for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize;
735              p_item < p_end && !save;
736              p_item++ )
737         {
738             vlc_mutex_lock (p_item->p_lock);
739             save = p_item->b_autosave && p_item->b_dirty;
740             vlc_mutex_unlock (p_item->p_lock);
741         }
742     }
743     module_list_free (list);
744
745     return save ? VLC_SUCCESS : SaveConfigFile( p_this, NULL, true );
746 }
747
748 int __config_SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name )
749 {
750     return SaveConfigFile( p_this, psz_module_name, false );
751 }