]> git.sesse.net Git - vlc/blob - src/config/file.c
config: remove leading underscores
[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 #undef config_LoadConfigFile
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     vlc_rwlock_wrlock (&config_lock);
193     while (fgets (line, 1024, file) != NULL)
194     {
195         /* Ignore comments and empty lines */
196         switch (line[0])
197         {
198             case '#':
199             case '\n':
200             case '\0':
201                 continue;
202         }
203
204         if (line[0] == '[')
205         {
206             char *ptr = strchr (line, ']');
207             if (ptr == NULL)
208                 continue; /* syntax error; */
209             *ptr = '\0';
210
211             /* New section ( = a given module) */
212             strcpy (section, line + 1);
213             module = NULL;
214
215             if ((psz_module_name == NULL)
216              || (strcmp (psz_module_name, section) == 0))
217             {
218                 for (int i = 0; list[i]; i++)
219                 {
220                     module_t *m = list[i];
221
222                     if ((strcmp (section, m->psz_object_name) == 0)
223                      && (m->i_config_items > 0)) /* ignore config-less modules */
224                     {
225                         module = m;
226                         if (psz_module_name != NULL)
227                             msg_Dbg (p_this,
228                                      "loading config for module \"%s\"",
229                                      section);
230                         break;
231                     }
232                 }
233             }
234
235             continue;
236         }
237
238         if (module == NULL)
239             continue; /* no need to parse if there is no matching module */
240
241         char *ptr = strchr (line, '\n');
242         if (ptr != NULL)
243             *ptr = '\0';
244
245         /* look for option name */
246         const char *psz_option_name = line;
247
248         ptr = strchr (line, '=');
249         if (ptr == NULL)
250             continue; /* syntax error */
251
252         *ptr = '\0';
253         const char *psz_option_value = ptr + 1;
254
255         /* try to match this option with one of the module's options */
256         for (size_t i = 0; i < module->confsize; i++)
257         {
258             module_config_t *p_item = module->p_config + i;
259
260             if ((p_item->i_type & CONFIG_HINT)
261              || strcmp (p_item->psz_name, psz_option_name))
262                 continue;
263
264             /* We found it */
265             errno = 0;
266
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             break;
305         }
306     }
307     vlc_rwlock_unlock (&config_lock);
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_Warn( 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     /* Configuration lock must be taken before vlcrc serializer below. */
536     vlc_rwlock_rdlock (&config_lock);
537
538     /* The temporary configuration file is per-PID. Therefore SaveConfigFile()
539      * should be serialized against itself within a given process. */
540     static vlc_mutex_t lock = VLC_STATIC_MUTEX;
541     vlc_mutex_lock (&lock);
542
543     int fd = utf8_open (temporary, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
544     if (fd == -1)
545     {
546         vlc_rwlock_unlock (&config_lock);
547         vlc_mutex_unlock (&lock);
548         module_list_free (list);
549         goto error;
550     }
551     file = fdopen (fd, "wt");
552     if (file == NULL)
553     {
554         vlc_rwlock_unlock (&config_lock);
555         close (fd);
556         vlc_mutex_unlock (&lock);
557         module_list_free (list);
558         goto error;
559     }
560
561     fprintf( file, "\xEF\xBB\xBF###\n###  " COPYRIGHT_MESSAGE "\n###\n\n"
562        "###\n### lines beginning with a '#' character are comments\n###\n\n" );
563
564     /* Ensure consistent number formatting... */
565     locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
566     locale_t baseloc = uselocale (loc);
567
568     /* We would take the config lock here. But this would cause a lock
569      * inversion with the serializer above and config_AutoSaveConfigFile().
570     vlc_rwlock_rdlock (&config_lock);*/
571
572     /* Look for the selected module, if NULL then save everything */
573     for( i_index = 0; (p_parser = list[i_index]) != NULL; i_index++ )
574     {
575         module_config_t *p_item, *p_end;
576
577         if( psz_module_name && strcmp( psz_module_name,
578                                        p_parser->psz_object_name ) )
579             continue;
580
581         if( !p_parser->i_config_items )
582             continue;
583
584         if( psz_module_name )
585             msg_Dbg( p_this, "saving config for module \"%s\"",
586                      p_parser->psz_object_name );
587
588         fprintf( file, "[%s]", p_parser->psz_object_name );
589         if( p_parser->psz_longname )
590             fprintf( file, " # %s\n\n", p_parser->psz_longname );
591         else
592             fprintf( file, "\n\n" );
593
594         for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize;
595              p_item < p_end;
596              p_item++ )
597         {
598             if ((p_item->i_type & CONFIG_HINT) /* ignore hint */
599              || p_item->b_removed              /* ignore deprecated option */
600              || p_item->b_unsaveable)          /* ignore volatile option */
601                 continue;
602
603             /* Do not save the new value in the configuration file
604              * if doing an autosave, and the item is not an "autosaved" one. */
605             bool b_retain = b_autosave && !p_item->b_autosave;
606
607             if (IsConfigIntegerType (p_item->i_type))
608             {
609                 int val = b_retain ? p_item->saved.i : p_item->value.i;
610                 if (p_item->i_type == CONFIG_ITEM_KEY)
611                 {
612                     char *psz_key = ConfigKeyToString (val);
613                     config_Write (file, p_item->psz_text, N_("key"),
614                                   val == p_item->orig.i,
615                                   p_item->psz_name, "%s",
616                                   psz_key ? psz_key : "");
617                     free (psz_key);
618                 }
619                 else
620                     config_Write (file, p_item->psz_text,
621                                   (p_item->i_type == CONFIG_ITEM_BOOL)
622                                       ? N_("boolean") : N_("integer"),
623                                   val == p_item->orig.i,
624                                   p_item->psz_name, "%d", val);
625                 p_item->saved.i = val;
626             }
627             else
628             if (IsConfigFloatType (p_item->i_type))
629             {
630                 float val = b_retain ? p_item->saved.f : p_item->value.f;
631                 config_Write (file, p_item->psz_text, N_("float"),
632                               val == p_item->orig.f,
633                               p_item->psz_name, "%f", val);
634                 p_item->saved.f = val;
635             }
636             else
637             {
638                 const char *psz_value = b_retain ? p_item->saved.psz
639                                                  : p_item->value.psz;
640                 bool modified;
641
642                 assert (IsConfigStringType (p_item->i_type));
643
644                 if (b_retain && (psz_value == NULL)) /* FIXME: hack */
645                     psz_value = p_item->orig.psz;
646
647                 modified =
648                     (psz_value != NULL)
649                         ? ((p_item->orig.psz != NULL)
650                             ? (strcmp (psz_value, p_item->orig.psz) != 0)
651                             : true)
652                         : (p_item->orig.psz != NULL);
653
654                 config_Write (file, p_item->psz_text, N_("string"),
655                               !modified, p_item->psz_name, "%s",
656                               psz_value ? psz_value : "");
657
658                 if ( !b_retain )
659                 {
660
661                     free ((char *)p_item->saved.psz);
662                     if( (psz_value && p_item->orig.psz &&
663                          strcmp( psz_value, p_item->orig.psz )) ||
664                         !psz_value || !p_item->orig.psz)
665                         p_item->saved.psz = strdupnull (psz_value);
666                     else
667                         p_item->saved.psz = NULL;
668                 }
669             }
670
671             if (!b_retain)
672                 p_item->b_dirty = false;
673         }
674     }
675     vlc_rwlock_unlock (&config_lock);
676
677     module_list_free (list);
678     if (loc != (locale_t)0)
679     {
680         uselocale (baseloc);
681         freelocale (loc);
682     }
683
684     /*
685      * Restore old settings from the config in file
686      */
687     fputs( p_bigbuffer, file );
688     free( p_bigbuffer );
689
690     /*
691      * Flush to disk and replace atomically
692      */
693     fflush (file); /* Flush from run-time */
694 #ifndef WIN32
695 #ifdef __APPLE__
696     fsync (fd); /* Flush from OS */
697 #else
698     fdatasync (fd); /* Flush from OS */
699 #endif
700     /* Atomically replace the file... */
701     if (utf8_rename (temporary, permanent))
702         utf8_unlink (temporary);
703     /* (...then synchronize the directory, err, TODO...) */
704     /* ...and finally close the file */
705     vlc_mutex_unlock (&lock);
706 #endif
707     fclose (file);
708 #ifdef WIN32
709     /* Windows cannot remove open files nor overwrite existing ones */
710     utf8_unlink (permanent);
711     if (utf8_rename (temporary, permanent))
712         utf8_unlink (temporary);
713     vlc_mutex_unlock (&lock);
714 #endif
715
716     free (temporary);
717     free (permanent);
718     return 0;
719
720 error:
721     if( file )
722         fclose( file );
723     free (temporary);
724     free (permanent);
725     free( p_bigbuffer );
726     return -1;
727 }
728
729 int config_AutoSaveConfigFile( vlc_object_t *p_this )
730 {
731     int ret = VLC_SUCCESS;
732     bool save = false;
733
734     assert( p_this );
735
736     /* Check if there's anything to save */
737     module_t **list = module_list_get (NULL);
738     vlc_rwlock_rdlock (&config_lock);
739     for (size_t i_index = 0; list[i_index] && !save; i_index++)
740     {
741         module_t *p_parser = list[i_index];
742         module_config_t *p_item, *p_end;
743
744         if( !p_parser->i_config_items ) continue;
745
746         for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize;
747              p_item < p_end && !save;
748              p_item++ )
749         {
750             save = p_item->b_autosave && p_item->b_dirty;
751         }
752     }
753
754     if (save)
755         /* Note: this will get the read lock recursively. Ok. */
756         ret = SaveConfigFile (p_this, NULL, true);
757     vlc_rwlock_unlock (&config_lock);
758
759     module_list_free (list);
760     return ret;
761 }
762
763 #undef config_SaveConfigFile
764 int config_SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name )
765 {
766     return SaveConfigFile( p_this, psz_module_name, false );
767 }